Ref's parentElement

Is it safe to do this? How do I make the parent reactive?
export const Child: Component = (props) => {
let ref!: HTMLElement;

onMount(() => {
ref.parentElement.addEventListener(/*... */);
});

return (
<div ref={ref} />
);
}
export const Child: Component = (props) => {
let ref!: HTMLElement;

onMount(() => {
ref.parentElement.addEventListener(/*... */);
});

return (
<div ref={ref} />
);
}
41 Replies
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
this feels like an anti-pattern what do u want to achieve?
deminearchiver
deminearchiverβ€’2mo ago
Just want to access a parent element As a default of a for prop
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
Mm sorry I still don't understand the intention
deminearchiver
deminearchiverβ€’2mo ago
my component requires passing a ref of another element
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
One edge case this pattern can't solve is if u do
const child = <Child />;
return <Show when={signal}>{child}</Show>
const child = <Child />;
return <Show when={signal}>{child}</Show>
https://playground.solidjs.com/anonymous/8a5ad155-f2bc-4af8-a404-e43762cf7410
deminearchiver
deminearchiverβ€’2mo ago
but often the element is the direct parent of the component element
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
can u show some pseudo-code? I am trying to understand why it requires passing a ref of another element.
deminearchiver
deminearchiverβ€’2mo ago
uhh the problem is that I'm not at my pc right now I'll try to type it on my phone but i cant promise it will be accurate
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
haha ja typing on the phone is brutal i just need the big picture
deminearchiver
deminearchiverβ€’2mo ago
Yes right Wait im dumb i have a repo with actual code I want this for
deminearchiver
deminearchiverβ€’2mo ago
i didnt want to send it because it has lots of external imports basically I want the default value for the for prop to be the parent element of the splash's div.
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
how would u use this component? it's a lot of code don't really understand what it is supposed to do
deminearchiver
deminearchiverβ€’2mo ago
normally I would do this:
<button ref={ref} >
<Splash for={ref} />
</button>
<button ref={ref} >
<Splash for={ref} />
</button>
its a splash ripple effect https://material-web.dev/components/ripple/
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
mm πŸ€” ye mb in this situation it's not a bad solution it's a bit bugprone
deminearchiver
deminearchiverβ€’2mo ago
Plus I want to use this component inside an astro component which does not allow refs to be passed I still think it's more of a solid question
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
i think the nice thing w solid is the fact that u do have that escape hatch of using vanilla js if u want to
deminearchiver
deminearchiverβ€’2mo ago
yes
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
for this type of api i wouldn't really know a better solution. if i would code it I would probably design it as a hook, but i can see the appeal as having it as a component.
peerreynders
peerreyndersβ€’2mo ago
I suspect the fundamental issue is that Web Components aren't Components i.e. this approach doesn't look kosher in the realm of JSX.
Web Components Aren’t Components
Modern web frameworks are built entirely on the concept of components. In the realm of these frameworks, a component is a reusable piece of application code. Al
deminearchiver
deminearchiverβ€’2mo ago
yes
REEEEE
REEEEEβ€’2mo ago
Does the code you posted not work?
peerreynders
peerreyndersβ€’2mo ago
I don't think we've even gotten to this part yet
How do I make the parent reactive?
I think in JSX Ripple would be implemented as a sizeless container to augment the child's behaviour on a DOM/CSS level. The entire notion of an AttachableController seems very OO composition inspired.
GitHub
material-web/internal/controller/attachable-controller.ts at main Β·...
Material Design Web Components. Contribute to material-components/material-web development by creating an account on GitHub.
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
Another pattern that is mb a bit more jsx-like is as="button" So instead of
<button ref={ref} >
<Splash for={ref} />
</button>
<button ref={ref} >
<Splash for={ref} />
</button>
it would be
<Splash as="button" />
<Splash as="button" />
or
<Splash as={Button} />
<Splash as={Button} />
peerreynders
peerreyndersβ€’2mo ago
As far as I can gather this would be easiest accomplished with a global event handler where elements can opt into the effect via their props/attributes. https://github.com/material-components/material-web/blob/main/ripple/internal/ripple.ts
GitHub
material-web/ripple/internal/ripple.ts at main Β· material-component...
Material Design Web Components. Contribute to material-components/material-web development by creating an account on GitHub.
deminearchiver
deminearchiverβ€’2mo ago
the point here is that the splash could be nested deep inside the button in which case i would need to use the ref Im not using the material web library, but trying to replicate its components in solidjs
deminearchiver
deminearchiverβ€’2mo ago
aa you can see, this part return the parent here if no element was to be attached https://github.com/material-components/material-web/blob/main/internal/controller/attachable-controller.ts#L129-L141
GitHub
material-web/internal/controller/attachable-controller.ts at main Β·...
Material Design Web Components. Contribute to material-components/material-web development by creating an account on GitHub.
deminearchiver
deminearchiverβ€’2mo ago
the code DOES work, but question is about inplementing a specific reactive feature
peerreynders
peerreyndersβ€’2mo ago
The question is: why is it in inside the button when it's the button that is supposed to show the ripple effect?
deminearchiver
deminearchiverβ€’2mo ago
I just followed the structure of material web ripple the button has position relative also the splash can be inset in the button: https://material-web.dev/components/ripple/stories/
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
ye that's a bit what peer is saying: the patterns of web-components might not translate that well to the patterns used in frontend-components. what would be the usecase of this feature?
peerreynders
peerreyndersβ€’2mo ago
The question is, given a working example like this: https://playground.solidjs.com/anonymous/2658dde3-9004-44ff-bd18-52a5f7b17b90 from How to Recreate the Ripple Effect of Material Design Buttons how to best package it for use for your particular SolidJS project (irrespective of what MD does).
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Bret Cameron
CSS-Tricks
How to Recreate the Ripple Effect of Material Design Buttons | CSS-...
Learn to make the ripple effect of Material Design's button component. We'll start with ES6+ JavaScript, before looking at other solutions.
deminearchiver
deminearchiverβ€’2mo ago
<div ref={ref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={ref} />
</div>
</div>
<div ref={ref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={ref} />
</div>
</div>
Deep nesting would allow for fine-grained positioning. For example, inside a navigation rail the touch area is large than the actual ripple. (on the image, the touch area of each destination is rectangular, but the splash is inset from the edges and has specific height / width values
No description
deminearchiver
deminearchiverβ€’2mo ago
I'm not quite sure what you meant here, but in my case the for is the element where the event listeners will be attached. I just want to know if this is OK to do:
createEffect(() => {
createEventListenerMap(
props.for ?? ref.parentElement,
{
click: onClick,
pointerdown: onPointerDown,
pointerup: onPointerUp,
pointerenter: onPointerEnter,
pointerleave: onPointerLeave,
pointercancel: onPointerCancel,
contextmenu: onContextmenu,
}
);
});
createEffect(() => {
createEventListenerMap(
props.for ?? ref.parentElement,
{
click: onClick,
pointerdown: onPointerDown,
pointerup: onPointerUp,
pointerenter: onPointerEnter,
pointerleave: onPointerLeave,
pointercancel: onPointerCancel,
contextmenu: onContextmenu,
}
);
});
Like in my understanding, the effect should get called when the parent of an element changes as well, like:
<Dynamic component={isLink() ? "a" : "button"}>
<Splash />
</Dynamic
<Dynamic component={isLink() ? "a" : "button"}>
<Splash />
</Dynamic
deminearchiver
deminearchiverβ€’2mo ago
deminearchiver
StackBlitz
Material Ripple - StackBlitz
A Solid TypeScript project based on @solid-primitives/event-listener, @solid-primitives/media, @solid-primitives/refs, @solid-primitives/utils, solid-js, typescript, vite and vite-plugin-solid
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
Like in my understanding, the effect should get called when the parent of an element changes as well, like
I don't think that pattern can be supported with the material design api. There is no event when a child gets attached to the parent. It is inherently bug prone. It will cover the 99% of the usecases, but you can't cover the 100% given this example
<div ref={ref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={ref} />
</div>
</div>
<div ref={ref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={ref} />
</div>
</div>
why not
<div onClick={() => console.log("Hello world!")}>
<Splash as="div" class="touch"/>
</div>
<div onClick={() => console.log("Hello world!")}>
<Splash as="div" class="touch"/>
</div>
?
deminearchiver
deminearchiverβ€’2mo ago
The div (touch) for example, could be inside a flexbox
bigmistqke 🌈
bigmistqke πŸŒˆβ€’2mo ago
A ye ofcourse the idea is that div with the ref should get the onClick-handler, i misread. Maybe
<Splash as="button" onClick={() => console.log('hello world!')}>
{ref => <div ref={ref} class="touch"/>}
</Splash>
<Splash as="button" onClick={() => console.log('hello world!')}>
{ref => <div ref={ref} class="touch"/>}
</Splash>
? Or maybe even
<Splash as="button" onClick={() => console.log('hello world!')}>
{className => <div class={clsx("touch", className)}/>}
</Splash>
<Splash as="button" onClick={() => console.log('hello world!')}>
{className => <div class={clsx("touch", className)}/>}
</Splash>
since it's only about visual styles anyway.
peerreynders
peerreyndersβ€’2mo ago
For what it is worth I believe that this style of authoring:
<div ref={ref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={ref} />
</div>
</div>
<div ref={ref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={ref} />
</div>
</div>
is miles ahead of what you proposed in your first post. Within the confines of a component you are just wiring together the parts of the component (i.e. the Splash component needed for the visual effect and the target element exist within the boundaries of the same component) β€”rather than blindly connecting things in the no man's land outside of the component (which to me smacks of inappropriate intimacy). So I'd refrain from stepping outside the component to grab a hold of parentElement; given that the container component could itself be a complex aggregation of elements that may not be the correct element anywayβ€”and it simply doesn't align with the β€œcomposition model” that β€œcomponents” are supposed to follow anyway. Supporting
<Dynamic component={isLink() ? "a" : "button"}>
<Splash />
</Dynamic>
<Dynamic component={isLink() ? "a" : "button"}>
<Splash />
</Dynamic>
in my view has poor ROI. Lots of additional complexity for relatively little (possibly unreliable) gain. Also if you look at the docs for Dynamic
<Dynamic component={options[selected()]} />
<Dynamic component={options[selected()]} />
So
<Dynamic component={isLink() ? SplashA : SplashButton} />
<Dynamic component={isLink() ? SplashA : SplashButton} />
is do-able and I think preferable as Splash lives and dies with the element it targets and decouples the Splash configuration of the components.
peerreynders
peerreyndersβ€’2mo ago
So just supporting
<div ref={targetRref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={targetRef} />
</div>
</div>
<div ref={targetRref} onClick={() => console.log("Hello world!")}>
<div class="touch">
<Splash for={targetRef} />
</div>
</div>
Should be the least complex and most bang for the buck use case that fits nicely into the component composition model and places emphasis on Simple is better than easy: β€œExplicit and consistent conventions even if they require more effort are worth it.”
SolidJS
Solid is a purely reactive library. It was designed from the ground up with a reactive core. It's influenced by reactive principles developed by previous libraries.
deminearchiver
deminearchiverβ€’2mo ago
hmm that makes sense
Want results from more Discord servers?
Add your server
More Posts