Search code examples
javascripthtmlcsssveltesvelte-5

How to get a component's children height?


I'm trying to create a component that will render it's children when it comes into the viewport, here's the code:

<script lang="ts">
    import type { Snippet } from 'svelte';
    import { viewport } from '$lib/actions/useViewportAction.svelte';

    let { children }: { children: Snippet } = $props();
    let animating = $state(false);

    function handleEnter() {
        console.log('Element entered the viewport');
        animating = true;
    }

    function handleLeave() {
        console.log('Element left the viewport');
        animating = false;
    }
</script>

<div use:viewport={{ onEnter: handleEnter, onLeave: handleLeave }}>
    {#if animating}
        {@render children()}
    {/if}
</div>

The outer div is the one that "detects" when the element enters the viewpor, and when that happens animating is set to true, which renders the children. This works but the problem is that before the user reaches the viewport the div is empty, so when you scroll to that point you can see the element apearing and displaising the elements bellow it.

I tryied fixing it several ways but I think the best solution would be to set the height of the reference div to the height of the children somehow. If this is not possible I would apreciate any alternative solutions.


Solution

  • The elements will not exist until the #if condition is true.

    Hence it can be better to not use #if but to conditionally set some CSS class that shows or hides an element. Especially if you want to prevent layout shifts it is useful to have the element already take up the intended space and be animated in visually.

    So e.g. something along the lines of:

    <div use:viewport={{ onEnter: handleEnter, onLeave: handleLeave }}
         class={['container', { show: animating }]}> <!-- Svelte >5.16 syntax -->
        {@render children()}
    </div>
    
    <style>
        .container {
            --duration: 0.3s;
            visibility: hidden;
            opacity: 0;
            transition:
                var(--duration) opacity,
                0s visibility var(--duration);
        }
        .container.show {
            visibility: visible;
            opacity: 1;
            transition: var(--duration) opacity;
        }
    </style>
    

    Playground

    Even if you were to measure the elements beforehand, if some container element is resized, the height might be affected leading to inaccuracies.