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

示例代码:

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


当前回答

我对这个问题的解决方案是设置一个突变观察者来检测<script></script>节点,然后用一个新的具有相同src的<script></script>节点替换它。例如:

let parentNode = /* node to observe */ void 0
let observer = new MutationObserver(mutations=>{
    mutations.map(mutation=>{
        Array.from(mutation.addedNodes).map(node=>{
            if ( node.parentNode == parentNode ) {
                let scripts = node.getElementsByTagName('script')
                Array.from(scripts).map(script=>{
                    let src = script.src
                    script = document.createElement('script')
                    script.src = src
                    return script
                })
            }
        })
    })
})
observer.observe(document.body, {childList: true, subtree: true});

其他回答

我有这个问题与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);

这里的解决方案不使用eval,与脚本、链接脚本以及模块一起工作。

该函数接受3个参数:

要插入的html代码的字符串 Dest:目标元素的引用 Append:在目标元素HTML的末尾启用追加的布尔标志

function insertHTML(html, dest, append=false){
    // if no append is requested, clear the target element
    if(!append) dest.innerHTML = '';
    // create a temporary container and insert provided HTML code
    let container = document.createElement('div');
    container.innerHTML = html;
    // cache a reference to all the scripts in the container
    let scripts = container.querySelectorAll('script');
    // get all child elements and clone them in the target element
    let nodes = container.childNodes;
    for( let i=0; i< nodes.length; i++) dest.appendChild( nodes[i].cloneNode(true) );
    // force the found scripts to execute...
    for( let i=0; i< scripts.length; i++){
        let script = document.createElement('script');
        script.type = scripts[i].type || 'text/javascript';
        if( scripts[i].hasAttribute('src') ) script.src = scripts[i].src;
        script.innerHTML = scripts[i].innerHTML;
        document.head.appendChild(script);
        document.head.removeChild(script);
    }
    // done!
    return true;
}

您必须使用eval()来执行作为DOM文本插入的任何脚本代码。

MooTools会自动为你做这件事,我相信jQuery也会(取决于版本)。jQuery 1.6+版本使用eval)。这节省了大量解析<script>标记和转义内容的麻烦,以及一堆其他“陷阱”。

通常情况下,如果你要自己eval()它,你想要创建/发送没有任何HTML标记的脚本代码,如<script>,因为这些将不能正确地eval()。

是的,你可以,但你必须在DOM之外做,而且顺序必须是正确的。

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    n.innerHTML = scr;
    document.body.appendChild(n);
}

...将提醒'foo'。这行不通:

document.getElementById("myDiv").innerHTML = scr;

即使这样也不行,因为先插入节点:

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    document.body.appendChild(n);
    n.innerHTML = scr;  
}

根据Danny '365CSI' Engelman的评论,这里有一个通用的解决方案:

<script>
  alert("This script always runs.");
  script01 = true;
</script>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
 onload="if(typeof script01==='undefined') eval(this.previousElementSibling.innerHTML)">

使用它作为innerHTML(即由XMLHttpRequest加载)或直接(即由PHP后端插入),脚本总是加载一次。

解释:脚本加载innerHTML不执行,但onload内容属性是。如果脚本没有执行(作为innerHTML添加),那么脚本将在image onload事件中执行。如果脚本已加载(由后端添加),则定义script01变量,onload将不会第二次运行脚本。