You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Svelte中如何获取动态加载HTML元素的宽高?

问题原因

你遇到的核心问题是:onMount钩子执行时机早于动态数据的渲染完成时间。静态内容在组件挂载时就已存在,所以onMount能拿到容器的真实宽高;但从数据库异步加载的page.data.personalMessage还未返回时,容器是空的,此时获取的尺寸自然是空白状态的错误值。


解决方案

方案1:父组件等待数据加载完成后再渲染打字机组件

在父组件中通过{#await}或{#if}确保数据存在后,再渲染TypewriterEffect,保证组件挂载时容器内已有完整内容。

修改+page.svelte:

<script>
  import { page } from '$app/state';
</script>
<div class="catch-phrase">
    {#await page.data}
        <!-- 可选:添加加载占位符 -->
        <div>加载中...</div>
    {:then data}
        <TypewriterEffect>
            <div>{data.personalMessage}</div>
        </TypewriterEffect>
    {/await}
</div>
<style>
    .catch-phrase {
        display: flex;
        align-items: center;
        margin: 0 10vw;
        max-width: 1200px;
        height: 90dvh;
    }
</style>

此时Typewriter.svelte的onMount就能正常获取到完整内容渲染后的宽高,原代码的onMount逻辑可以保留(因为组件挂载时内容已存在)。


方案2:子组件监听内容变化,延迟获取尺寸

如果不想修改父组件,可以在Typewriter.svelte中使用Svelte的$effect监听容器内容变化,等内容实际渲染后再执行尺寸获取逻辑。

修改Typewriter.svelte:

<script>
    import { $effect } from 'svelte';
    let container;
    let fullNode;
    let isReady = false;
    let chunkSize = 1; // 根据你的需求调整分片大小
    let timeout;
    let callback = () => {}; // 自定义完成回调

    // 监听容器内容变化,确保内容存在后再处理
    $effect(() => {
        if (!container || !container.children.length) return;

        // 克隆完整内容节点
        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 && isReady) {
                        console.log('launch !!');
                        typewriter();
                        observer.disconnect();
                    }
                });
            },
            { root: null, threshold: 1 }
        );
        observer.observe(container);

        // 清理函数
        return () => {
            observer.disconnect();
            clearTimeout(timeout);
        };
    });

    // 文本分片函数(补全你原代码中缺失的逻辑)
    const splitHtmlText = (text, size) => {
        return text.split('').reduce((acc, char, idx) => {
            if (idx % size === 0) acc.push([]);
            acc[acc.length - 1].push(char);
            return acc;
        }, []).map(chunk => chunk.join(''));
    };

    const typewriter = async () => {
        if (!fullNode) return;
        for (let i = 0; i < fullNode.childNodes.length; i++) {
            if (fullNode.childNodes[i].nodeType === 1) {
                const fullHtmlText = fullNode.childNodes[i].textContent;
                const splittedText = splitHtmlText(fullHtmlText, chunkSize);
                container.appendChild(fullNode.childNodes[i]);
                container.childNodes[i].textContent = '';
                await type(container.childNodes[i], splittedText, 0);
            }
        }
    };

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

内容的提问来源于stack exchange,提问作者16ar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 12:31:08