向innerHTML插入脚本时,带有DOMContentLoaded的脚本不会被执行

问题描述 投票:0回答:1

我制作了一个脚本,它获取 html 文件的文本并将其插入到另一个 html 文件中;我这样做是为了在进行更改时不需要更新每个页面上的标题和侧边栏。该脚本可以插入 html,但文件中的某些脚本一旦插入到主 html 中就不会运行。

function includeHTML() {
    let elements = document.querySelectorAll("[include-html]");

    for (let i = 0; i < elements.length; i++) { 
        const elem = elements[i];
        let file = elem.getAttribute("include-html");
        
        fetch(file).then(response => response.text()).then(data => {
            elem.innerHTML = data; // inject html
            
            let scripts = elem.querySelectorAll("script");
            for (let i = 0; i < scripts.length; i++) { // find scripts
                let script = scripts[i];
                let newScript = document.createElement("script");

                let attrs = script.attributes;
                for (let i = 0; i < attrs.length; i++) { // copy attributes
                    attr = attrs[i];
                    newScript.setAttribute(attr.name, attr.value);
                };
                newScript.innerHTML = script.innerHTML // copy internal script
                script.replaceWith(newScript); // replace script
            };
        }); 
        elem.removeAttribute("include-html");
    };
};

includeHTML();

^这是我的脚本,插入到innerHTML后,它替换了脚本,这使一些工作有效,但不能使其他工作有效

<html>
    <head>
        <script defer src="/include.js"></script>
    </head>
    <body>
        <div include-html="/file.html"></div>
    </body>
</html>

^ 这是我如何使用它的示例

如果插入的脚本使用 DOMContentLoaded 我猜它是在它已经被触发之后插入的,所以它不会被执行,我发现的一个解决方案是在一秒钟后手动触发 DOMContentLoaded 但我觉得这可能取决于用户的计算机/互联网速度,并且如果页面上其他位置的任何脚本捕获第一次触发,它会使它们运行两次,所以这肯定不是最好的方法

顺便说一下,我在 neocities 上托管,所以我没有任何后端访问权限,提前感谢您的帮助

javascript html dom innerhtml
1个回答
0
投票

当您使用 JavaScript 将 HTML 内容动态插入到基础 HTML 文件中时,HTML 中包含的某些脚本不会按预期执行。特别是,依赖 DOMContentLoaded 事件或其他初始化触发器的脚本不会执行,因为在插入内容时该事件已被触发。这会导致交互式组件在不手动更新或触发事件的情况下无法正确初始化。

为了确保动态包含的 HTML 中的脚本正确执行,可以实现自定义事件。此方法涉及在插入 HTML 内容后发送自定义事件,并且包含的脚本必须侦听此事件以初始化它们。

工作原理:

  • 包含后发送自定义事件:接收并插入 HTML 内容后,主脚本发送自定义事件(例如 htmlInincluded)以表示包含过程完成。

  • 监听包含的脚本中的自定义事件:包含的 HTML 文件中的脚本监听自定义事件而不是 DOMContentLoaded。当接收到事件时,这些脚本执行它们的初始化代码。

此方法确保在 HTML 内容完全插入 DOM 后运行初始化代码,无论原始 DOMContentLoaded 事件何时发生。

示例代码:

function includeHTML() {
    let elements = document.querySelectorAll("[include-html]");
    let promises = Array.from(elements).map(elem => {
        let file = elem.getAttribute("include-html");
        return fetch(file)
            .then(response => response.text())
            .then(data => {
                elem.innerHTML = data; // Inject HTML

                // Replace and execute scripts
                let scripts = elem.querySelectorAll("script");
                scripts.forEach(oldScript => {
                    let newScript = document.createElement("script");

                    // Copy attributes
                    Array.from(oldScript.attributes).forEach(attr => {
                        newScript.setAttribute(attr.name, attr.value);
                    });

                    newScript.textContent = oldScript.textContent;
                    oldScript.replaceWith(newScript);
                });

                elem.removeAttribute("include-html");
            })
            .catch(err => console.error(`Error including ${file}:`, err));
    });

    Promise.all(promises).then(() => {
        // Dispatch the custom event after all includes are done
        document.dispatchEvent(new Event('htmlIncluded'));
    });
}

includeHTML();
<!-- file.html -->
<script>
    document.addEventListener('htmlIncluded', function() {
        // Initialization code that was previously inside DOMContentLoaded
        console.log('Included script initialized');
        // Your initialization logic here
    });
</script>
© www.soinside.com 2019 - 2024. All rights reserved.