执行使用.innerHTML插入的<script>元素
我有一个脚本,它使用innerHTML
将一些内容插入到一个元素中。
内容可能是例如:
<script type="text/javascript">alert('test');</script> <strong>test</strong>
问题是<script>
标签内的代码没有得到执行。 我GOOGLE了一下,但没有明显的解决办法。 如果我使用jQuery $(element).append(content);
插入$(element).append(content);
脚本部分在被注入到DOM之前已经被eval
了。
有没有人得到执行所有<script>
元素的代码片段? jQuery代码有点复杂,所以我无法弄清楚它是如何完成的。
编辑 :
通过偷看到jQuery代码,我已经设法弄清楚jQuery是如何做到的,这导致了下面的代码:
Demo: <div id="element"></div> <script type="text/javascript"> function insertAndExecute(id, text) { domelement = document.getElementById(id); domelement.innerHTML = text; var scripts = []; ret = domelement.childNodes; for ( var i = 0; ret[i]; i++ ) { if ( scripts && nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); } } for(script in scripts) { evalScript(scripts[script]); } } function nodeName( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); } function evalScript( elem ) { data = ( elem.text || elem.textContent || elem.innerHTML || "" ); var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; script.appendChild( document.createTextNode( data ) ); head.insertBefore( script, head.firstChild ); head.removeChild( script ); if ( elem.parentNode ) { elem.parentNode.removeChild( elem ); } } insertAndExecute("element", "<scri"+"pt type='text/javascript'>document.write('This text should appear as well.')</scr"+"ipt><strong>this text should also be inserted.</strong>"); </script>
OP的脚本在IE 7中不起作用。在SO的帮助下,这里有一个脚本:
exec_body_scripts: function(body_el) { // Finds and executes scripts in a newly added element's body. // Needed since innerHTML does not run scripts. // // Argument body_el is an element in the dom. function nodeName(elem, name) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }; function evalScript(elem) { var data = (elem.text || elem.textContent || elem.innerHTML || "" ), head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; try { // doesn't work on ie... script.appendChild(document.createTextNode(data)); } catch(e) { // IE has funky script nodes script.text = data; } head.insertBefore(script, head.firstChild); head.removeChild(script); }; // main section of function var scripts = [], script, children_nodes = body_el.childNodes, child, i; for (i = 0; children_nodes[i]; i++) { child = children_nodes[i]; if (nodeName(child, "script" ) && (!child.type || child.type.toLowerCase() === "text/javascript")) { scripts.push(child); } } for (i = 0; scripts[i]; i++) { script = scripts[i]; if (script.parentNode) {script.parentNode.removeChild(script);} evalScript(scripts[i]); } };
@phidah …这是一个非常有趣的解决你的问题: http : //24ways.org/2005/have-your-dom-and-script-it-too
所以它会看起来像这样:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
试试这个片段:
function stripAndExecuteScript(text) { var scripts = ''; var cleaned = text.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){ scripts += arguments[1] + '\n'; return ''; }); if (window.execScript){ window.execScript(scripts); } else { var head = document.getElementsByTagName('head')[0]; var scriptElement = document.createElement('script'); scriptElement.setAttribute('type', 'text/javascript'); scriptElement.innerText = scripts; head.appendChild(scriptElement); head.removeChild(scriptElement); } return cleaned; }; var scriptString = '<scrip' + 't + type="text/javascript">alert(\'test\');</scr' + 'ipt><strong>test</strong>'; document.getElementById('element').innerHTML = stripAndExecuteScript(scriptString);
这是一个更短,更高效的脚本,也适用于具有src
属性的脚本:
function insertAndExecute(id, text) { document.getElementById(id).innerHTML = text; var scripts = Array.prototype.slice.call(document.getElementById(id).getElementsByTagName("script")); for (var i = 0; i < scripts.length; i++) { if (scripts[i].src != "") { var tag = document.createElement("script"); tag.src = scripts[i].src; document.getElementsByTagName("head")[0].appendChild(tag); } else { eval(scripts[i].innerHTML); } } }
注意:虽然如果使用不当, eval
可能会导致安全漏洞,但比运行中创build脚本标签要快得多。
您不应使用innerHTML属性,而应使用Node的appendChild方法:文档树[HTML DOM]中的节点。 这样你可以稍后调用你的注入代码。
确保您明白node.innerHTML
与 node.appendChild
。 您可能需要花费一些时间在Javascript客户端参考资料中了解更多细节和DOM。 希望以下帮助…
样品注射工作:
<html> <head> <title>test</title> <script language="javascript" type="text/javascript"> function doOnLoad(){ addScript('inject',"function foo(){ alert('injected'); }"); } function addScript(inject,code){ var _in = document.getElementById('inject'); var scriptNode = document.createElement('script'); scriptNode.innerHTML = code; _in.appendChild(scriptNode); } </script> </head> <body onload="doOnLoad();"> <div id="header">some content</div> <div id="inject"></div> <input type="button" onclick="foo(); return false;" value="Test Injected" /> </body> </html>
问候,
function insertHtml(id, html) { var ele = document.getElementById(id); ele.innerHTML = html; var codes = ele.getElementsByTagName("script"); for(var i=0;i<codes.length;i++) { eval(codes[i].text); } }
它在我的项目中的Chrome中工作
scriptNode.innerHTML = code
不适用于IE。 唯一要做的就是用scriptNode.text = code
replace它,它工作正常
不使用“eval”的解决scheme:
var setInnerHtml = function(elm, html) { elm.innerHTML = html; var scripts = elm.getElementsByTagName("script"); // If we don't clone the results then "scripts" // will actually update live as we insert the new // tags, and we'll get caught in an endless loop var scriptsClone = []; for (var i = 0; i < scripts.length; i++) { scriptsClone.push(scripts[i]); } for (var i = 0; i < scriptsClone.length; i++) { var currentScript = scriptsClone[i]; var s = document.createElement("script"); // Copy all the attributes from the original script for (var j = 0; j < currentScript.attributes.length; j++) { var a = currentScript.attributes[j]; s.setAttribute(a.name, a.value); } s.appendChild(document.createTextNode(currentScript.innerHTML)); currentScript.parentNode.replaceChild(s, currentScript); } }
这基本上克隆了脚本标记,然后用新生成的脚本标记replace了被阻止的脚本标记,从而允许执行。
使用jquery $(parent).html(code)
代替parent.innerHTML = code
更容易:
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 }) }
这也适用于使用document.write
和通过src
属性加载的脚本的脚本。 不幸的是,即使这不适用于Google AdSense脚本。
你可以看看这个post 。 代码可能如下所示:
var actualDivToBeUpdated = document.getElementById('test'); var div = document.createElement('div'); div.innerHTML = '<script type="text/javascript">alert("test");<\/script>'; var children = div.childNodes; actualDivToBeUpdated.innerHTML = ''; for(var i = 0; i < children.length; i++) { actualDivToBeUpdated.appendChild(children[i]); }
感谢拉里的脚本,在IE10中工作得很好,这是我用过的:
$('#' + id)[0].innerHTML = result; $('#' + id + " script").each(function() { this.text = this.text || $(this).text();} );
尝试函数eval()。
data.newScript = '<script type="text/javascript">//my script...</script>' var element = document.getElementById('elementToRefresh'); element.innerHTML = data.newScript; eval(element.firstChild.innerHTML);
这是我正在开发的一个项目的一个实例。 感谢这个职位
延伸拉里的。 我使它recursion地search整个块和子节点。
该脚本现在还将调用使用src参数指定的外部脚本。 脚本被追加到头部,而不是插入和放置的顺序,他们被发现。 所以特别是订单脚本被保留。 并且每个脚本都被同步执行,类似于浏览器如何处理最初的DOM加载。 所以,如果你有一个脚本块从CDN调用jQuery,而下一个脚本节点使用jQuery …没有问题! 哦,我使用序列化的ID标记附加的脚本,基于你在标签参数中设置的内容,所以你可以find这个脚本添加的内容。
exec_body_scripts: function(body_el, tag) { // Finds and executes scripts in a newly added element's body. // Needed since innerHTML does not run scripts. // // Argument body_el is an element in the dom. function nodeName(elem, name) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }; function evalScript(elem, id, callback) { var data = (elem.text || elem.textContent || elem.innerHTML || "" ), head = document.getElementsByTagName("head")[0] || document.documentElement; var script = document.createElement("script"); script.type = "text/javascript"; if (id != '') { script.setAttribute('id', id); } if (elem.src != '') { script.src = elem.src; head.appendChild(script); // Then bind the event to the callback function. // There are several events for cross browser compatibility. script.onreadystatechange = callback; script.onload = callback; } else { try { // doesn't work on ie... script.appendChild(document.createTextNode(data)); } catch(e) { // IE has funky script nodes script.text = data; } head.appendChild(script); callback(); } }; function walk_children(node) { var scripts = [], script, children_nodes = node.childNodes, child, i; if (children_nodes === undefined) return; for (i = 0; i<children_nodes.length; i++) { child = children_nodes[i]; if (nodeName(child, "script" ) && (!child.type || child.type.toLowerCase() === "text/javascript")) { scripts.push(child); } else { var new_scripts = walk_children(child); for(j=0; j<new_scripts.length; j++) { scripts.push(new_scripts[j]); } } } return scripts; } var i = 0; function execute_script(i) { script = scripts[i]; if (script.parentNode) {script.parentNode.removeChild(script);} evalScript(scripts[i], tag+"_"+i, function() { if (i < scripts.length-1) { execute_script(++i); } }); } // main section of function if (tag === undefined) tag = 'tmp'; var scripts = walk_children(body_el); execute_script(i); }
试试这个,它适用于我的Chrome,Safari和Firefox:
var script = document.createElement('script'); script.innerHTML = 'console.log("hi")'; document.body.appendChild(script); --> logs "hi"
有一点需要注意的是,下面的div嵌套脚本不会运行:
var script = document.createElement('div'); script.innerHTML = '<script>console.log("hi")</script>'; document.body.appendChild(script); --> doesn't log anything
要运行脚本,必须将其创build为一个节点,然后添加为一个子节点。 你甚至可以在一个先前注入的div中追加一个脚本,它会运行(我试图让广告服务器代码工作之前遇到过这个问题):
var div = document.createElement('div'); div.id = 'test-id'; document.body.appendChild(div); var script = document.createElement('script'); script.innerHTML = 'console.log("hi")'; document.getElementById('test-id').appendChild(script); --> logs "hi"
做就是了:
document.body.innerHTML = '<img src="..http://img.dovov.comloaded.gif" alt="" onload="alert(\'test\');this.parentNode.removeChild(this);" />';