S
SolidJS•16mo ago
bakaotaku

How to reactively set height of a element?

The title is kinda vague so let me explain. I have a h1 tag that has 2 divs inside it. I want to set the height of the parent h1 tag equal to height of single child div. I've kept overflow hidden so the extra div is hidden. My component looks like this:
import { Motion } from "@motionone/solid";
import { createSignal, For } from "solid-js";
import pageStore from "~/store/page";

export default function Home() {
const [ref, setRef] = createSignal<HTMLElement>();
const [_, setPage] = pageStore;

return (
<Motion.section
class="flex items-center justify-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => {
setPage("about");
}}
>
<h1
style={
ref() &&
(console.log(ref()!.getBoundingClientRect().height),
{
height: `${ref()!.getBoundingClientRect().height - 2}px`,
})
}
class="overflow-hidden text-center text-9xl font-semibold"
>
<For each={["Baka Otaku", "Tanish Khare"]}>
{(word) => (
<div ref={setRef}>
<For each={word.split("")}>
{(letter, idx) => (
<Motion.span
class="inline-block letterSpan"
animate={{ y: "-100%" }}
transition={{
y: {
duration: 1,
delay: idx() * 0.03,
easing: [0.76, 0, 0.024, 1],
},
}}
>
{letter.trim() === "" ? "\xa0" : letter}
</Motion.span>
)}
</For>
</div>
)}
</For>
</h1>
</Motion.section>
);
}
import { Motion } from "@motionone/solid";
import { createSignal, For } from "solid-js";
import pageStore from "~/store/page";

export default function Home() {
const [ref, setRef] = createSignal<HTMLElement>();
const [_, setPage] = pageStore;

return (
<Motion.section
class="flex items-center justify-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => {
setPage("about");
}}
>
<h1
style={
ref() &&
(console.log(ref()!.getBoundingClientRect().height),
{
height: `${ref()!.getBoundingClientRect().height - 2}px`,
})
}
class="overflow-hidden text-center text-9xl font-semibold"
>
<For each={["Baka Otaku", "Tanish Khare"]}>
{(word) => (
<div ref={setRef}>
<For each={word.split("")}>
{(letter, idx) => (
<Motion.span
class="inline-block letterSpan"
animate={{ y: "-100%" }}
transition={{
y: {
duration: 1,
delay: idx() * 0.03,
easing: [0.76, 0, 0.024, 1],
},
}}
>
{letter.trim() === "" ? "\xa0" : letter}
</Motion.span>
)}
</For>
</div>
)}
</For>
</h1>
</Motion.section>
);
}
5 Replies
bakaotaku
bakaotakuOP•16mo ago
The issue is that initially when the component mounts it works as expected, but when the component unounts and comes back the resizing logic doesn't work. As you can see in the component I have a console.log, it gives the expected value on first mount but 0 on remount.... for the mount and unmount thing I'm using Switch component which tracks a signal.
<Presence exitBeforeEnter>
<Switch>
<Match when={page() === "home"}>
<Home />
</Match>
<Match when={page() === "about"}>
<About />
</Match>
</Switch>
</Presence>
<Presence exitBeforeEnter>
<Switch>
<Match when={page() === "home"}>
<Home />
</Match>
<Match when={page() === "about"}>
<About />
</Match>
</Switch>
</Presence>
bakaotaku
bakaotakuOP•16mo ago
a small recording to show the issue.
bakaotaku
bakaotakuOP•16mo ago
UPDATE: This seems to work but can someone explain why this works? 🤔 :
export default function Home() {
const [ref, setRef] = createSignal<HTMLElement>();
const [_, setPage] = pageStore;

const [height, setHeight] = createSignal(0);

createEffect(() => {
ref() && setHeight(ref()!.getBoundingClientRect().height - 2);
});

return (
<Motion.section
class="flex items-center justify-center" // First time hidden
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => {
setPage("about");
}}
>
<h1
style={{
height: `${height()}px`,
}}
class="overflow-hidden text-center text-9xl font-semibold"
>
<For each={["Baka Otaku", "Tanish Khare"]}>
{(word) => (
<div ref={setRef}>
<For each={word.split("")}>
{(letter, idx) => (
<Motion.span
class="inline-block letterSpan"
animate={{ y: "-100%" }}
transition={{
y: {
duration: 1,
delay: idx() * 0.03,
easing: [0.76, 0, 0.024, 1],
},
}}
>
{letter.trim() === "" ? "\xa0" : letter}
</Motion.span>
)}
</For>
</div>
)}
</For>
</h1>
</Motion.section>
);
}
export default function Home() {
const [ref, setRef] = createSignal<HTMLElement>();
const [_, setPage] = pageStore;

const [height, setHeight] = createSignal(0);

createEffect(() => {
ref() && setHeight(ref()!.getBoundingClientRect().height - 2);
});

return (
<Motion.section
class="flex items-center justify-center" // First time hidden
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => {
setPage("about");
}}
>
<h1
style={{
height: `${height()}px`,
}}
class="overflow-hidden text-center text-9xl font-semibold"
>
<For each={["Baka Otaku", "Tanish Khare"]}>
{(word) => (
<div ref={setRef}>
<For each={word.split("")}>
{(letter, idx) => (
<Motion.span
class="inline-block letterSpan"
animate={{ y: "-100%" }}
transition={{
y: {
duration: 1,
delay: idx() * 0.03,
easing: [0.76, 0, 0.024, 1],
},
}}
>
{letter.trim() === "" ? "\xa0" : letter}
</Motion.span>
)}
</For>
</div>
)}
</For>
</h1>
</Motion.section>
);
}
instead of directly using the ref height value in style I update a signal in createEffect and then use it in style instead..
ai6
ai6•16mo ago
I am also curious about this. It might have something to do with tracking since it only runs on page load. I recently had a similar issue where a resource wouldn't load when it was put in a <Switch> block. I had assumed that that's a tracking context but I guess not. Maybe it's similar for you with <Motion>. I am curious what happens when you call the ref ouside of the Motion block:

<>
{!ref() && <></>}
<Motion.section>
...

<>
{!ref() && <></>}
<Motion.section>
...
bakaotaku
bakaotakuOP•16mo ago
the issue persists even without a motion block
Want results from more Discord servers?
Add your server