Search code examples
sveltesveltekit

Getting height of dynamically added HTML element


I retrieve HTML text from a database and I am performing some animation to display the content. In order to keep layout clean I want to get the height and width of the element and empty it before starting my animation. But the onMount call does give me dimensions like it is empty.

I get the correct dimensions if the text is static but not dynamic. Is there anything I can do to avoid this ?

//+page.svelte
<script>
  import { page } from '$app/state';
</script>
<div class="catch-phrase">
    <TypewriterEffect>
        <div>
            { page.data.personalMessage}
        </div>
    </TypewriterEffect>
</div>
<style>
    .catch-phrase {
        display: flex;
        align-items: center;
        margin: 0 10vw;
        max-width: 1200px;
        height: 90dvh;
    }
</style>
//Typewriter.svelte
<script>
    onMount(() => {
        fullNode = container.cloneNode(true);
        container.style.height = container.offsetHeight + 'px';
        container.style.width = container.offsetWidth + 'px';
        container.innerHTML = '';
        isReady = true;
        const observer = new IntersectionObserver(
            (entries) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        console.log('launch !!');
                        typewriter();
                        observer.disconnect();
                    }
                });
            },
            {
                root: null, // viewport
                threshold: 1 // percentage of element visible
            }
        );
        observer.observe(container);
    });
    const typewriter = async () => {
        var fullHtmlText = '';
        for (var i = 0; i < fullNode.childNodes.length; i++) {
            if (fullNode.childNodes[i].nodeType === 1) {
                fullHtmlText = fullNode.childNodes[i].textContent;
                splittedText = splitHtmlText(fullHtmlText, chunkSize);
                container.appendChild(fullNode.childNodes[i]);
                container.childNodes[i].textContent = '';
                await type(container.childNodes[i], fullHtmlText, 0);
            }
        }

        function type(node, fullHtmlText, currentIndex) {
            return new Promise((resolve) => {
                if (node.textContent.length >= fullHtmlText.length) {
                    resolve();
                    clearTimeout(timeout);
                    callback();
                    return;
                }
                node.innerHTML = splittedText.slice(0, currentIndex).join('');
                var timeout = setTimeout(() => type(node, fullHtmlText, currentIndex + 1), 10);
            });
        }
        return {
            destroy() {
                clearTimeout(timeout);
            }
        };
    };
}
</script>
<div bind:this={container}>{@render children?.()}</div>

Here is a screenshot of the conatainer height :

Thanks for your help.


Solution

  • Apparently the problem comes from the page.data and seems to be a bug (posted here and here.

    Using data.personalMessage message instead of page.data.personalMessage is both a workaround and a better practice.

    //+page.svelte
    <script>
       const { data } = $props();
    </script>
    <TypewriterEffect>
       {@html data.personalMessage}
    </TypewriterEffect>
    

    Thanks to @brunnerh for the solution.