Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/88.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/lua/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 可以使用innerHTML插入脚本吗?_Javascript_Html_Dom_Innerhtml - Fatal编程技术网

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标记的脚本代码,例如,因为这些HTML标记无法正确评估脚本代码。

可以,但必须在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;  
}

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

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

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

现在使用jqueryjqueryjs运行eval应该已经包括在内了

   $('.javascript').each(function() {
      eval($(this).text());

    });`

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

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

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div

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

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.htmlcode代替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
    })
}

对于仍在尝试这样做的人,不,您不能使用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。但这一原则是有效的


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

下面是一个递归函数,用于设置我在广告服务器中使用的元素的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);
    }
}

您可以看到演示。

我解决这个问题的方法是设置一个检测节点,然后用一个具有相同src的新节点替换它。例如:

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

尝试使用模板和document.importNode。以下是一个例子:

样品 样品 var div=document.createElementdiv; var t=document.createElement'template'; t、 innerHTML=检查javascript输出的控制台选项卡:Hello world!!!控制台。记录“你好,世界!!!”;; 对于var i=0;i你可以这样做:

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

Gabriel Garcia提到的变异观察者是正确的,但对我来说不太管用。我不确定这是因为浏览器的怪癖还是因为我这边的一个错误,但最终适用于我的版本如下:

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});
};
当然,您应该用正在修改的元素的名称替换元素_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]);
    }
}
以及加载函数开始的示例:

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
}

这里是一个不使用eval的解决方案,它可以与脚本、链接脚本以及模块一起使用

该函数接受3个参数:

html:带有html代码t的字符串 o插入 dest:对目标元素的引用 append:启用在目标元素html末尾追加的布尔标志 您也可以这样包装您的文件,它将被执行:

<your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';

请注意:srcdoc中的作用域指的是iframe,因此您必须像上面的示例中那样使用top来访问父文档。

我在innerHTML中遇到了这个问题,我必须将一个Hotjar脚本附加到我的Reactjs应用程序的head标记中,它必须在附加后立即执行

将动态节点导入头标记的一个很好的解决方案是模块

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

innerHTML中没有脚本标记

事实证明,HTML5不允许使用innerHTML属性动态添加脚本标记。因此,以下命令将不会执行,也不会出现向世界问好的警报

element.innerHTML = "<script>alert('Hello World!')</script>";
和内联脚本:

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

对我来说,最好的方法是通过innerHtml插入新的HTML内容,然后使用

setTimeout(() => {
        var script_el = document.createElement("script")
        script_el.src = 'script-to-add.js'
        document.body.appendChild(script_el)
    }, 500)

setTimeout不是必需的,但它工作得更好。这对我很有用。

每次我想动态插入脚本标记时,我都会这样做

  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)">
常量html= ` 根据Danny'365CSI'Engelman的评论,这里有一个通用的解决方案:

将其用作innerHTML,即由XMLHttpRequest加载,或直接由PHP后端插入,脚本始终加载一次


说明:不会执行作为innerHTML加载的脚本,但会执行onload content atribute。如果脚本未作为innerHTML执行,则脚本将在图像加载事件中执行。如果脚本是由后端添加的,则定义了script01变量,onload将不会第二次运行脚本。

筛选脚本标记并使用eval运行每个标记

var tmp=document.createElement'div'; tmp.innerHTML='alerthello>'; […tmp.children].filterx=>x.nodeName==='SCRIPT'.forEachx=>evalx.innerText;

我真正想做的是加载一个外部脚本,而不仅仅是评估一些本地脚本。使用innerHTML添加脚本标记比创建脚本DOM元素并将其添加到正文中要短得多,我正在尝试使代码尽可能短。您是否必须创建dom脚本元素并将它们添加到dom中,而不仅仅是使用innerHTML之类的东西?有没有办法在函数中使用document.write实现这一点?正如zombat所建议的那样,使用Javascript框架加载外部脚本,不要试图重新发明轮子。JQuery使这变得非常简单,只需包含JQuery和call:$.getScripturl。您还可以提供一个回调函数,该函数将在加载脚本后执行。我很感谢您尽量保持代码简短,并且添加带有innerHTML的标记可能很短,但不起作用。在通过eval之前都是纯文本。不幸的是,eval不解析HTML标记,所以你会遇到一连串的问题。eval不是解决任何问题的好方法。我自己也尝试过eval。这是一个可怕的想法。每次你都要评估整个事情。即使您声明了一个变量名和值,您也必须每次重新声明/重新评估它以使其工作。这是一场错误的噩梦。除非文档中没有任何其他脚本元素。改用document.documentElement。不是必需的,因为您正在从另一个脚本编写脚本。var g=document.createElement'script';var s=document.getElementsByTagName'script'[0]//参考此脚本g.text=alert\hi\;s、 parentNode.insertBeforeg,s;谁说是另一个剧本?您可以在不使用元素的情况下运行JavaScript。例如,和。如果你想成为一名技术人员,这在非HTML/SVG文档中也不起作用,因为名称空间不明确。Facebook在SDK中使用Pablo的答案。值得一提的是:这在当前的浏览器上似乎不起作用。我有点惊讶,你的答案竟然是这么低。这是最好的解决方案,这种方法甚至允许您限制带有特定URL或内容的脚本。@inf3rno有或没有?它过去是有效的,有人说过不同的话吗?[0]的目的是什么?能否使用nodeScriptReplacedocument.getElementById.html@是的。你可以。在IWebBrowser2中似乎没有帮助;我可以确认使用createElement重新创建脚本标记,但我仍然无法通过InvokeScript.Thank调用它们。它修复了我在使用TinyBox2 Jquery插件创建的模式弹出窗口中添加Disqs通用代码的问题。不幸的是,当脚本包含稍后将调用的函数时,此解决方案不起作用。当插入ajax请求的结果时,对我不起作用:语法错误丢失;在脚本开头的before语句string如何添加img src。。。行到您的页面代码?您是使用document.write方法还是使用document.body.innerHTML+=方法?对我来说,这两种方法都是失败的:在onload属性中编写大量代码不太实际。还有这个

s需要一个额外的文件才能存在和加载。这不是一种妥协。两个缺点:1。它无法调用通过InnerHtmlAllong添加的带有此IMG标记的任何脚本/函数,因为就浏览器而言,它们不存在2。如果.removeChild之前的部分内联代码引发异常,img元素将不会被删除。您可以对触发器映像进行Base64编码,因为这实际上不会执行网络请求。。。您不需要图像,引用一个不存在的图像,而不是使用onload-use-onerror,但这将使网络请求在此处延迟一点,但是任何可能使用此方法的人,请注意,在JQuery中,您需要使用$.loadScripturl加载脚本,而不是我刚刚尝试过的,它可以在chrome 57上工作。脚本标记上的innerHTML执行文本。有趣的是,它以前不工作。我想知道这种行为是跨浏览器还是仅在chrome 57中出现。这在Microsoft Edge上不起作用,还有其他解决方法吗?谢谢你在没有说明原因的情况下对我投了反对票。解释:这个直接添加到HTML中的代码片段可以确保将来作为innerHTML添加的任何外部脚本都会被解析。我觉得这是一个很好的通用想法,但我没有放弃,因为我担心document.createElement添加的脚本是否可能执行两次?它还让我害怕改变变异对象——我不确定它在某些情况下是否不能生成无限循环。你注意到每次文档中添加一个元素时,你都会添加一个新的变异观察者,对吗?顺便说一句,我想知道你为什么说我的代码不起作用。@gabrielgarcia我说你的代码不起作用,因为我试过了,但根本不起作用。现在看来,这完全有可能是我的错,而不是你的错,我真诚地为我的措辞道歉。re:每次向文档中添加元素时,都会添加一个MutationObserver,您在说什么?DOMContentLoaded,我在这里引用MDN,在初始HTML文档完全加载和解析时激发,而不等待样式表、图像和子框架完成加载。那是一次,而且只有一次。此外,这个脚本在我的网站上运行没有问题,调试表明它只发生一次,所以在实践和理论上都是一次。。。我骑错了。我也很抱歉。@gabrielgarcia没问题:我是说。。。在脚本标签上附加代码内容是一种评估,不是吗?@KevinB存在着臭名昭著的差异。。。尝试eval'console.logthis',您将看到最明显的一个,因此上下文不同,并且?这仍然只是评估。@KevinB不,这不是评估。试试这个评估‘让b=100’。。然后尝试从评估外部访问b。。。。祝你好运,你会需要它为我工作。Cheers您的newScript元素具有,因此您可以使用newScript.text=alert'Hello World!'设置内联代码;不需要创建和附加新的文本节点。@马丁:当然,在编程世界中有很多不同的方法来实现这些东西!而且,这更清楚,更易于维护:.tnx,这是2016年的答案。我现在要做的是:onload=console.log21,this/>
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)">