我尝试在<div>上使用innerHTML加载一些脚本到页面中。脚本似乎加载到DOM中,但它从未执行(至少在Firefox和Chrome中)。有一种方法让脚本执行时插入他们与innerHTML?

示例代码:

<!DOCTYPE html > < html > <身体onload = " . getelementbyid(机)。innerHTML = '<script>alert(\'hi\')<\/script>'"> 难道不应该出现“hi”的提醒吗? < div id = "装载机" > < / div > 身体< / > < / html >


当前回答

Gabriel Garcia提到的mutationobserver是正确的,但对我来说不太管用。我不确定这是因为浏览器的怪癖还是因为我自己的错误,但最终适合我的版本是:

document.addEventListener("DOMContentLoaded", function(event) {
    var observer = new MutationObserver(mutations=>{
        mutations.map(mutation=>{
            Array.from(mutation.addedNodes).map(node=>{
                if (node.tagName === "SCRIPT") {
                    var s = document.createElement("script");
                    s.text=node.text;
                    if (typeof(node.parentElement.added) === 'undefined')
                        node.parentElement.added = [];
                    node.parentElement.added[node.parentElement.added.length] = s;
                    node.parentElement.removeChild(node);
                    document.head.appendChild(s);
                }
            })
        })
    })
    observer.observe(document.getElementById("element_to_watch"), {childList: true, subtree: true,attributes: false});
};

当然,您应该将element_to_watch替换为要修改的元素的名称。

node.parentElement.added用于存储添加到document.head的脚本标记。在用于加载外部页面的函数中,您可以使用如下内容删除不再相关的脚本标记:

function freeScripts(node){
    if (node === null)
        return;
    if (typeof(node.added) === 'object') {
        for (var script in node.added) {
            document.head.removeChild(node.added[script]);
        }
        node.added = {};
    }
    for (var child in node.children) {
        freeScripts(node.children[child]);
    }
}

这是一个load函数开始的例子:

function load(url, id, replace) {
    if (document.getElementById(id) === null) {
        console.error("Element of ID "+id + " does not exist!");
        return;
    }
    freeScripts(document.getElementById(id));
    var xhttp = new XMLHttpRequest();
    // proceed to load in the page and modify innerHTML
}

其他回答

尝试使用template和document.importNode。这里有一个例子:

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Sample</title> </head> <body> <h1 id="hello_world">Sample</h1> <script type="text/javascript"> var div = document.createElement("div"); var t = document.createElement('template'); t.innerHTML = "Check Console tab for javascript output: Hello world!!!<br/><script type='text/javascript' >console.log('Hello world!!!');<\/script>"; for (var i=0; i < t.content.childNodes.length; i++){ var node = document.importNode(t.content.childNodes[i], true); div.appendChild(node); } document.body.appendChild(div); </script> </body> </html>

我有这个问题与innerHTML,我不得不追加一个Hotjar脚本的“头部”标签我的Reactjs应用程序,它将必须立即执行追加后。

将动态节点导入“head”标签的一个很好的解决方案是React-helment模块。


此外,对于所提出的问题,还有一个有用的解决方案:

在innerHTML中没有脚本标记!

事实证明,HTML5不允许使用innerHTML属性动态添加脚本标记。因此,下面的代码将不会执行,也不会出现Hello World!

element.innerHTML = "<script>alert('Hello World!')</script>";

这在HTML5规范中有记录:

注意:使用innerHTML插入的脚本元素不执行时 它们是插入的。

但是要注意,这并不意味着innerHTML不受跨站点脚本的影响。可以通过innerHTML执行JavaScript,而不使用MDN的innerHTML页面上所示的标记。

解决方案:动态添加脚本

要动态添加脚本标记,您需要创建一个新的脚本元素并将其附加到目标元素。

你可以为外部脚本这样做:

var newScript = document.createElement("script");
newScript.src = "http://www.example.com/my-script.js";
target.appendChild(newScript);

和内联脚本:

var newScript = document.createElement("script");
var inlineScript = document.createTextNode("alert('Hello World!');");
newScript.appendChild(inlineScript); 
target.appendChild(newScript);

Gabriel Garcia提到的mutationobserver是正确的,但对我来说不太管用。我不确定这是因为浏览器的怪癖还是因为我自己的错误,但最终适合我的版本是:

document.addEventListener("DOMContentLoaded", function(event) {
    var observer = new MutationObserver(mutations=>{
        mutations.map(mutation=>{
            Array.from(mutation.addedNodes).map(node=>{
                if (node.tagName === "SCRIPT") {
                    var s = document.createElement("script");
                    s.text=node.text;
                    if (typeof(node.parentElement.added) === 'undefined')
                        node.parentElement.added = [];
                    node.parentElement.added[node.parentElement.added.length] = s;
                    node.parentElement.removeChild(node);
                    document.head.appendChild(s);
                }
            })
        })
    })
    observer.observe(document.getElementById("element_to_watch"), {childList: true, subtree: true,attributes: false});
};

当然,您应该将element_to_watch替换为要修改的元素的名称。

node.parentElement.added用于存储添加到document.head的脚本标记。在用于加载外部页面的函数中,您可以使用如下内容删除不再相关的脚本标记:

function freeScripts(node){
    if (node === null)
        return;
    if (typeof(node.added) === 'object') {
        for (var script in node.added) {
            document.head.removeChild(node.added[script]);
        }
        node.added = {};
    }
    for (var child in node.children) {
        freeScripts(node.children[child]);
    }
}

这是一个load函数开始的例子:

function load(url, id, replace) {
    if (document.getElementById(id) === null) {
        console.error("Element of ID "+id + " does not exist!");
        return;
    }
    freeScripts(document.getElementById(id));
    var xhttp = new XMLHttpRequest();
    // proceed to load in the page and modify innerHTML
}

Krasimir Tsonev有一个伟大的解决方案,可以克服所有的问题。 他的方法不需要使用eval,因此不存在性能和安全问题。 它允许你用js设置innerHTML字符串包含html,并立即将其转换为DOM元素,同时还执行代码中存在的js部分。简短,简单,完全按你想要的工作。

享受他的解决方案吧:

http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element

重要提示:

您需要用div标签包装目标元素 你需要用div标签包装src字符串。 如果你直接写src字符串,它包括js部分,请注意正确地写结束脚本标记(在/之前有\),因为这是一个字符串。

下面是一个递归地将所有脚本替换为可执行脚本的方法:

function nodeScriptReplace(node) {
        if ( nodeScriptIs(node) === true ) {
                node.parentNode.replaceChild( nodeScriptClone(node) , node );
        }
        else {
                var i = -1, children = node.childNodes;
                while ( ++i < children.length ) {
                      nodeScriptReplace( children[i] );
                }
        }

        return node;
}
function nodeScriptClone(node){
        var script  = document.createElement("script");
        script.text = node.innerHTML;

        var i = -1, attrs = node.attributes, attr;
        while ( ++i < attrs.length ) {                                    
              script.setAttribute( (attr = attrs[i]).name, attr.value );
        }
        return script;
}

function nodeScriptIs(node) {
        return node.tagName === 'SCRIPT';
}

示例调用:

nodeScriptReplace(document.getElementsByTagName("body")[0]);