Javascript 可以使用innerHTML插入脚本吗?

Javascript 可以使用innerHTML插入脚本吗?,javascript,html,dom,innerhtml,Javascript,Html,Dom,Innerhtml,我试图使用上的innerHTML将一些脚本加载到页面中。脚本似乎加载到DOM中,但从未执行(至少在Firefox和Chrome中)。在使用innerHTML插入脚本时,是否有办法让脚本执行 示例代码: 难道不应该出现说“嗨”的警告吗? 必须使用来执行作为DOM文本插入的任何脚本代码 MooTools会自动为您完成这项工作,我相信jQuery也会这样做(取决于版本。jQuery 1.6+版使用eval)。这就省去了解析标记、转义内容以及一大堆其他“陷阱”的麻烦 通常,如果要自己创建/发送脚本代

我试图使用
上的
innerHTML
将一些脚本加载到页面中。脚本似乎加载到DOM中,但从未执行(至少在Firefox和Chrome中)。在使用
innerHTML
插入脚本时,是否有办法让脚本执行

示例代码:


难道不应该出现说“嗨”的警告吗?
必须使用来执行作为DOM文本插入的任何脚本代码

MooTools会自动为您完成这项工作,我相信jQuery也会这样做(取决于版本。jQuery 1.6+版使用
eval
)。这就省去了解析
标记、转义内容以及一大堆其他“陷阱”的麻烦


通常,如果要自己创建/发送脚本代码,则需要创建/发送不带任何HTML标记的脚本代码,例如
,因为这些不会正确地
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);
}
即使这样也不行,因为节点是先插入的:

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

以下是一个非常有趣的解决方案:

因此,请使用此标记而不是脚本标记:

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]);
    }
}

您可以创建脚本,然后注入内容

var g = document.createElement('script');
var s = document.getElementsByTagName('script')[0];
g.text = "alert(\"hi\");"
s.parentNode.insertBefore(g, s);
这适用于所有浏览器:)从innerHTML执行(Java脚本)标记

用具有class属性class=“javascript”的div替换脚本元素,并用

不要更改要执行的内容(以前它在script标记中,现在在div标记中)

在页面中添加样式

.javascript{display:none;}

现在使用jquery运行eval(应该已经包括jquery js)


你可以在我的博客上浏览更多信息。

我使用了这段代码,它运行良好

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div
var arr=MyDiv.getElementsByTagName('script'))
对于(变量n=0;n
这里有一种方法,可以递归地用可执行脚本替换所有脚本:

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';
}

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

享受他的解决方案:

重要提示:

  • 您需要用div标记包装目标元素
  • 您需要用div标记包装src字符串
  • 如果您直接编写src字符串并且它包含js部分,请注意正确地编写结束脚本标记(使用\before/),因为这是一个字符串 使用
    $(parent).html(code)
    而不是
    parent.innerHTML=code

    下面还修复了使用
    document.write
    和通过
    src
    属性加载的脚本。不幸的是,即使这样,谷歌AdSense脚本也不起作用

    var oldDocumentWrite = document.write;
    var oldDocumentWriteln = document.writeln;
    try {
        document.write = function(code) {
            $(parent).append(code);
        }
        document.writeln = function(code) {
            document.write(code + "<br/>");
        }
        $(parent).html(html); 
    } finally {
        $(window).load(function() {
            document.write = oldDocumentWrite
            document.writeln = oldDocumentWriteln
        })
    }
    
    var oldDocumentWrite=document.write;
    var oldDocumentWriteln=document.writeln;
    试一试{
    document.write=函数(代码){
    $(父项).附加(代码);
    }
    document.writeln=函数(代码){
    文件。写入(代码+“
    ”); } $(父).html(html); }最后{ $(窗口)。加载(函数(){ document.write=oldDocumentWrite document.writeln=旧文档writeln }) }

    对于仍在尝试这样做的任何人,不,您不能使用
    innerHTML
    注入脚本,但可以使用
    Blob
    URL.createObjectURL
    将字符串加载到脚本标记中

    我创建了一个示例,让您可以将字符串作为脚本运行,并通过承诺返回脚本的“导出”:

    function loadScript(scriptContent, moduleId) {
        // create the script tag
        var scriptElement = document.createElement('SCRIPT');
    
        // create a promise which will resolve to the script's 'exports'
        // (i.e., the value returned by the script)
        var promise = new Promise(function(resolve) {
            scriptElement.onload = function() {
                var exports = window["__loadScript_exports_" + moduleId];
                delete window["__loadScript_exports_" + moduleId];
                resolve(exports);
            }
        });
    
        // wrap the script contents to expose exports through a special property
        // the promise will access the exports this way
        var wrappedScriptContent =
            "(function() { window['__loadScript_exports_" + moduleId + "'] = " + 
            scriptContent + "})()";
    
        // create a blob from the wrapped script content
        var scriptBlob = new Blob([wrappedScriptContent], {type: 'text/javascript'});
    
        // set the id attribute
        scriptElement.id = "__loadScript_module_" + moduleId;
    
        // set the src attribute to the blob's object url 
        // (this is the part that makes it work)
        scriptElement.src = URL.createObjectURL(scriptBlob);
    
        // append the script element
        document.body.appendChild(scriptElement);
    
        // return the promise, which will resolve to the script's exports
        return promise;
    }
    
    ...
    
    function doTheThing() {
        // no evals
        loadScript('5 + 5').then(function(exports) {
             // should log 10
            console.log(exports)
        });
    }
    
    我已经从我的实际实现中简化了这一点,因此没有承诺其中没有任何bug。但这一原则是有效的


    如果您不关心在脚本运行后返回任何值,那么就更容易了;只需省略
    承诺
    加载
    位。您甚至不需要包装脚本或创建全局
    窗口。\uuuuu加载\uu脚本\uu导出\uu
    属性。

    下面是一个递归函数,用于设置我在广告服务器中使用的元素的innerHTML:

    // o: container to set the innerHTML
    // html: html text to set.
    // clear: if true, the container is cleared first (children removed)
    function setHTML(o, html, clear) {
        if (clear) o.innerHTML = "";
    
        // Generate a parseable object with the html:
        var dv = document.createElement("div");
        dv.innerHTML = html;
    
        // Handle edge case where innerHTML contains no tags, just text:
        if (dv.children.length===0){ o.innerHTML = html; return; }
    
        for (var i = 0; i < dv.children.length; i++) {
            var c = dv.children[i];
    
            // n: new node with the same type as c
            var n = document.createElement(c.nodeName);
    
            // copy all attributes from c to n
            for (var j = 0; j < c.attributes.length; j++)
                n.setAttribute(c.attributes[j].nodeName, c.attributes[j].nodeValue);
    
            // If current node is a leaf, just copy the appropriate property (text or innerHTML)
            if (c.children.length == 0)
            {
                switch (c.nodeName)
                {
                    case "SCRIPT":
                        if (c.text) n.text = c.text;
                        break;
                    default:
                        if (c.innerHTML) n.innerHTML = c.innerHTML;
                        break;
                }
            }
            // If current node has sub nodes, call itself recursively:
            else setHTML(n, c.innerHTML, false);
            o.appendChild(n);
        }
    }
    
    //o:用于设置innerHTML的容器
    //html:要设置的html文本。
    //清除:如果为true,则首先清除容器(移除子容器)
    函数setHTML(o,html,clear){
    如果(清除)o.innerHTML=“”;
    //使用html生成可解析的对象:
    var dv=document.createElement(“div”);
    dv.innerHTML=html;
    //处理innerHTML不包含标记,只包含文本的边缘情况:
    if(dv.children.length==0){o.innerHTML=html;return;}
    对于(变量i=0;ifunction loadScript(scriptContent, moduleId) {
        // create the script tag
        var scriptElement = document.createElement('SCRIPT');
    
        // create a promise which will resolve to the script's 'exports'
        // (i.e., the value returned by the script)
        var promise = new Promise(function(resolve) {
            scriptElement.onload = function() {
                var exports = window["__loadScript_exports_" + moduleId];
                delete window["__loadScript_exports_" + moduleId];
                resolve(exports);
            }
        });
    
        // wrap the script contents to expose exports through a special property
        // the promise will access the exports this way
        var wrappedScriptContent =
            "(function() { window['__loadScript_exports_" + moduleId + "'] = " + 
            scriptContent + "})()";
    
        // create a blob from the wrapped script content
        var scriptBlob = new Blob([wrappedScriptContent], {type: 'text/javascript'});
    
        // set the id attribute
        scriptElement.id = "__loadScript_module_" + moduleId;
    
        // set the src attribute to the blob's object url 
        // (this is the part that makes it work)
        scriptElement.src = URL.createObjectURL(scriptBlob);
    
        // append the script element
        document.body.appendChild(scriptElement);
    
        // return the promise, which will resolve to the script's exports
        return promise;
    }
    
    ...
    
    function doTheThing() {
        // no evals
        loadScript('5 + 5').then(function(exports) {
             // should log 10
            console.log(exports)
        });
    }
    
    // o: container to set the innerHTML
    // html: html text to set.
    // clear: if true, the container is cleared first (children removed)
    function setHTML(o, html, clear) {
        if (clear) o.innerHTML = "";
    
        // Generate a parseable object with the html:
        var dv = document.createElement("div");
        dv.innerHTML = html;
    
        // Handle edge case where innerHTML contains no tags, just text:
        if (dv.children.length===0){ o.innerHTML = html; return; }
    
        for (var i = 0; i < dv.children.length; i++) {
            var c = dv.children[i];
    
            // n: new node with the same type as c
            var n = document.createElement(c.nodeName);
    
            // copy all attributes from c to n
            for (var j = 0; j < c.attributes.length; j++)
                n.setAttribute(c.attributes[j].nodeName, c.attributes[j].nodeValue);
    
            // If current node is a leaf, just copy the appropriate property (text or innerHTML)
            if (c.children.length == 0)
            {
                switch (c.nodeName)
                {
                    case "SCRIPT":
                        if (c.text) n.text = c.text;
                        break;
                    default:
                        if (c.innerHTML) n.innerHTML = c.innerHTML;
                        break;
                }
            }
            // If current node has sub nodes, call itself recursively:
            else setHTML(n, c.innerHTML, false);
            o.appendChild(n);
        }
    }
    
    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});
    
    var mydiv = document.getElementById("mydiv");
    var content = "<script>alert(\"hi\");<\/script>";
    
    mydiv.innerHTML = content;
    var scripts = mydiv.getElementsByTagName("script");
    for (var i = 0; i < scripts.length; i++) {
        eval(scripts[i].innerText);
    }
    
    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});
    };
    
    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]);
        }
    }
    
    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
    }
    
    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;
    }
    
    <your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';
    
    element.innerHTML = "<script>alert('Hello World!')</script>";
    
    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);
    
    setTimeout(() => {
            var script_el = document.createElement("script")
            script_el.src = 'script-to-add.js'
            document.body.appendChild(script_el)
        }, 500)
    
      const html =
        `<script>
            alert('Building up on Danny '365CSI' Engelman's comment, here is an universal solution:

    <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)">