Javascript 执行用 .innerHTML 插入的 <script> 元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2592092/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Executing <script> elements inserted with .innerHTML
提问by phidah
I've got a script that inserts some content into an element using innerHTML.
我有一个脚本,它使用innerHTML.
The content could for example be:
例如,内容可以是:
<script type="text/javascript">alert('test');</script>
<strong>test</strong>
Problem is that the code inside the <script>tag doesn't get executed.
I googled it a bit but there were no apparent solutions. If I inserted the content using jQuery $(element).append(content);the script parts got eval'd before being injected into the DOM.
问题是<script>标签内的代码没有被执行。我用谷歌搜索了一下,但没有明显的解决方案。如果我使用 jQuery 插入内容$(element).append(content);,脚本部分eval在被注入到 DOM 之前就被 d 了。
Has anyone got a snippet of code that executes all the <script>elements? The jQuery code was a bit complex so I couldn't really figure out how it was done.
有没有人有一段执行所有<script>元素的代码?jQuery 代码有点复杂,所以我无法真正弄清楚它是如何完成的。
Edit:
编辑:
By peeking into the jQuery code I've managed to figure out how jQuery does it, which resulted in the following code:
通过查看 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>
采纳答案by Larry K
The OP's script doesn't work in IE 7. With help from SO, here's a script that does:
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]);
}
};
回答by user447963
@phidah... Here is a very interesting solution to your problem: http://24ways.org/2005/have-your-dom-and-script-it-too
@phidah...这是一个非常有趣的问题解决方案:http: //24ways.org/2005/have-your-dom-and-script-it-too
So it would look like this instead:
所以它看起来像这样:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
回答by DividedByZero
Here's a shorter, more efficient script that also works for scripts with the srcproperty:
这是一个更短、更高效的脚本,它也适用于具有该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);
}
}
}
Note: whilst evalmay cause a security vulnerability if not used properly, it is muchfaster than creating a script tag on the fly.
注意:虽然eval如果使用不当可能会导致安全漏洞,但它比动态创建脚本标签要快得多。
回答by Andreas
You should not use the innerHTML property but rather the appendChild method of the Node: a node in a document tree [HTML DOM]. This way you are able to later call your injected code.
您不应使用 innerHTML 属性,而应使用 Node 的 appendChild 方法:文档树 [HTML DOM] 中的一个节点。这样您就可以稍后调用注入的代码。
Make sure that you understand that node.innerHTMLis not the same asnode.appendChild. You might want to spend some time on the Javascript Client Reference for more details and the DOM. Hope the following helps...
确保您理解这node.innerHTML与node.appendChild. 您可能想花一些时间在 Javascript Client Reference 上了解更多详细信息和 DOM。希望以下内容有帮助...
Sample injection works:
进样工作:
<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>
regards,
问候,
回答by allenhwkim
Simplified ES6 version of @joshcomley's answer with an example.
带有示例的@joshcomley 答案的简化 ES6 版本。
No JQuery, No library, No eval, No DOM change, Just pure Javascript.
没有 JQuery,没有库,没有 eval,没有 DOM 更改,只是纯 Javascript。
http://plnkr.co/edit/MMegiu?p=preview
http://plnkr.co/edit/MMegiu?p=preview
var setInnerHTML = function(elm, html) {
elm.innerHTML = html;
Array.from(elm.querySelectorAll("script")).forEach( oldScript => {
const newScript = document.createElement("script");
Array.from(oldScript.attributes)
.forEach( attr => newScript.setAttribute(attr.name, attr.value) );
newScript.appendChild(document.createTextNode(oldScript.innerHTML));
oldScript.parentNode.replaceChild(newScript, oldScript);
});
}
Usage
用法
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);
.innerHTML = HTML; // does *NOT* run <script> tags in HTML
setInnerHTML(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);
}
}
, HTML); // does run <script> tags in HTML
回答by fantactuka
Try this snippet:
试试这个片段:
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);
}
}
回答by Bruce
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
})
}
It works in Chrome in my project
它在我的项目中的 Chrome 中工作
回答by joshcomley
A solution without using "eval":
不使用“eval”的解决方案:
##代码##This essentially clones the script tag and then replaces the blocked script tag with the newly generated one, thus allowing execution.
这实质上是克隆脚本标签,然后用新生成的脚本标签替换被阻止的脚本标签,从而允许执行。
回答by iirekm
It's easier to use jquery $(parent).html(code)instead of parent.innerHTML = code:
使用 jquery$(parent).html(code)而不是更容易parent.innerHTML = code:
This also works with scripts that use document.writeand scripts loaded via srcattribute. Unfortunately even this doesn't work with Google AdSense scripts.
这也适用于使用document.write和通过src属性加载的脚本的脚本。不幸的是,即使这也不适用于 Google AdSense 脚本。
回答by Jorge
scriptNode.innerHTML = codedidn't work for IE. The only thing to do is replace with scriptNode.text = codeand it work fine
scriptNode.innerHTML = code不适用于 IE。唯一要做的就是替换为scriptNode.text = code,它工作正常

