我尝试在innerHTML
上使用<div>
将一些脚本加载到页面中。看起来脚本加载到DOM中,但它永远不会被执行(至少在Firefox和Chrome中)。使用innerHTML
插入脚本时是否有办法执行脚本?
示例代码:
<!DOCTYPE html>
<html>
<body onload="document.getElementById('loader').innerHTML = '<script>alert(\'hi\')<\/script>'">
Shouldn't an alert saying 'hi' appear?
<div id="loader"></div>
</body>
</html>
答案 0 :(得分:79)
这是一个非常有趣的解决方案: http://24ways.org/2005/have-your-dom-and-script-it-too
因此请使用此代替脚本标记:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
答案 1 :(得分:74)
您必须使用eval()来执行您作为DOM文本插入的任何脚本代码。
MooTools会自动为您完成此操作,我确信jQuery也会这样(取决于版本.jQuery版本1.6+使用eval
)。这节省了解析<script>
标签和转义内容以及其他一些“陷阱”的麻烦。
通常,如果您要自己eval()
,则需要创建/发送脚本代码而不使用任何HTML标记,例如<script>
,因为这些标记不会eval()
正确。
答案 2 :(得分:68)
这是一个以递归方式替换所有脚本的方法:
function nodeScriptReplace(node) {
if ( nodeScriptIs(node) === true ) {
node.parentNode.replaceChild( nodeScriptClone(node) , node );
}
else {
var i = 0;
var children = node.childNodes;
while ( i < children.length ) {
nodeScriptReplace( children[i++] );
}
}
return node;
}
function nodeScriptIs(node) {
return node.tagName === 'SCRIPT';
}
function nodeScriptClone(node){
var script = document.createElement("script");
script.text = node.innerHTML;
for( var i = node.attributes.length-1; i >= 0; i-- ) {
script.setAttribute( node.attributes[i].name, node.attributes[i].value );
}
return script;
}
示例电话:
nodeScriptReplace(document.getElementsByTagName("body")[0]);
答案 3 :(得分:38)
您可以创建脚本然后注入内容。
var g = document.createElement('script');
var s = document.getElementsByTagName('script')[0];
g.text = "alert(\"hi\");"
s.parentNode.insertBefore(g, s);
适用于所有浏览器:)
答案 4 :(得分:25)
我使用了这段代码,工作正常
var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
eval(arr[n].innerHTML)//run script inside div
答案 5 :(得分:5)
每次我想动态插入脚本标签时,我都会这样做!
const html =
`<script>
alert('? there ! Wanna grab a ?');
</script>`;
const scriptEl = document.createRange().createContextualFragment(html);
parent.append(scriptEl);
注意:已使用ES6
编辑1:
为大家澄清-我已经看到很多答案都使用appendChild
,并想让大家知道它的工作原理与append
答案 6 :(得分:4)
对于仍在尝试执行此操作的任何人,不,您不能使用innerHTML
注入脚本,但可以使用Blob
和{{1将字符串加载到脚本代码中}}
我创建了一个示例,它允许您将字符串作为脚本运行,并获取通过承诺返回的脚本的“导出”:
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)
});
}
和Promise
位。您甚至不需要包装脚本或创建全局onload
属性。
答案 7 :(得分:2)
过滤你的脚本标签并用 eval 运行它们
var tmp= document.createElement('div');
tmp.innerHTML = '<script>alert("hello")></script>';
[...tmp.children].filter(x => x.nodeName === 'SCRIPT').forEach(x => eval(x.innerText));
答案 8 :(得分:2)
这是一个递归函数,用于设置我在广告服务器中使用的元素的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);
}
}
您可以看到演示here。
答案 9 :(得分:1)
Krasimir Tsonev有一个很好的解决方案可以解决所有问题。 他的方法不需要使用eval,因此不存在性能和安全问题。 它允许您设置innerHTML字符串包含带有js的html并立即将其转换为DOM元素,同时还执行沿代码存在的js部分。简短,简单,完全按照您的意愿工作。
享受他的解决方案:
http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element
重要说明:
答案 10 :(得分:1)
使用$(parent).html(code)
代替parent.innerHTML = code
。
以下内容还修复了使用document.write
的脚本和通过src
属性加载的脚本。不幸的是,即使这不适用于Google 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
})
}
答案 11 :(得分:0)
我对此问题的解决方案是设置Mutation Observer以检测<script></script>
个节点,然后将其替换为具有相同src的新<script></script>
节点。例如:
let parentNode = /* your node */ 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});
答案 12 :(得分:0)
以 Danny'365CSI'Engelman的评论为基础,这是一种通用解决方案:
(SELECT ...
SUM(B) as Sum_of_transactions, COUNT(B) as Number_of_transactions
...
GROUP BY A, C)
将此脚本用作innerHTML(即由XMLHttpRequest加载)或直接(即由PHP后端插入),该脚本始终加载一次。
说明:不执行作为innerHTML加载的脚本,而是执行onload内容属性。如果未执行脚本(添加为innerHTML),则脚本将在图像加载事件中执行。如果脚本已加载(由后端添加),则将定义<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)">
变量,并且onload将不会第二次运行脚本。
答案 13 :(得分:0)
innerHTML遇到了这个问题,我不得不将Hotjar脚本附加到我的Reactjs应用程序的“ head”标签中,并且必须在附加后立即执行。
React-helment模块是将动态节点导入“ head”标签的一种很好的解决方案。
对于所提出的问题,还有一个有用的解决方案:
innerHTML中没有脚本标签!
事实证明,HTML5不允许使用innerHTML属性动态添加脚本标签。因此,以下操作将不会执行,并且不会发出警告“ Hello World”!
element.innerHTML = "<script>alert('Hello World!')</script>";
这已记录在HTML5规范中:
注意:使用innerHTML插入的脚本元素在以下情况下不执行 它们被插入。
但是要注意,这并不意味着innerHTML可以安全地进行跨站点脚本编写。可以通过innerHTML执行JavaScript,而无需使用MDN's innerHTML page所示的标签。
解决方案:动态添加脚本
要动态添加脚本标签,您需要创建一个新的脚本元素并将其附加到目标元素。
您可以为外部脚本执行此操作:
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);
答案 14 :(得分:0)
您还可以像这样包装itemList.forEach { reservation ->
val entityObj = reservation.getParseObject("object_entity")
if (entityObj == null) createLog("FoundObj", "null") else
createLog("FoundObj", entityObj.objectId.toString())
}
,它将被执行:
<script>
请注意:<your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';
中的范围是指iframe,因此您必须像上例中那样使用srcdoc
来访问父文档。
答案 15 :(得分:0)
这里的解决方案不使用first_list = [
('1234', 'abcd', 'John Doe', 'good_status'),
('1234', 'efgh', 'John Doe', 'good_status'),
('1234', 'hijk', 'John Doe', 'bad_status'),
('5566', 'abjk', 'George Washington', 'good_status'),
('7889', 'zyxw', 'Jane Austin', 'bad_status')
]
second_list = [
('1234', 'John Doe', 'abcd efgh hijk'),
('5566', 'George Washington', 'abjk'),
('7889', 'Jane Austin', 'zyxw')
]
def CreateDict(list1, list2):
#Dictionary to be created.
dictionary = {}
#Go through each given data.
for search_key in list2:
dict_data = [search_key[1]]
status = ""
#Go through the data we must check for the status.
for element in list1:
#If the number and the names match.
if search_key[0] == element[0] and search_key[1] == element[2]:
#Check the status of each data.
data = search_key[2].split(" ")
for d in data:
if d in element:
# Data| status without the _status | add a space at the end.
status += d+"_"+element[3].replace("_status", "") +" "
#Remove the last space added on the status string.
status = status[:len(status)-1]
#Append the status data on the list.
dict_data.append(status)
#Append the dict_data using the number as a key.
dictionary[str(search_key[0])] = dict_data
#Return the dictionary.
return dictionary
print(CreateDict(first_list, second_list))
,并且可以使用脚本,链接脚本以及模块 。
该函数接受3个参数:
eval
答案 16 :(得分:0)
加布里埃尔·加西亚(Gabriel Garcia)提到MutationObservers的方向是正确的,但是它既不起作用,也不是有效的代码。这是一个正确的例子:
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});
};
当然,您应该将element_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
}
答案 17 :(得分:0)
您可以这样做:
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);
}
答案 18 :(得分:0)
尝试使用template和document.importNode。这是一个例子:
{{1}}
答案 19 :(得分:0)
是的,你可以,但你必须在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;
}
答案 20 :(得分:-1)
对我来说,最好的方法是通过innerHtml插入新的HTML内容,然后使用
setTimeout(() => {
var script_el = document.createElement("script")
script_el.src = 'script-to-add.js'
document.body.appendChild(script_el)
}, 500)
不需要setTimeout,但是效果更好。这对我有用。
答案 21 :(得分:-2)
从innerHTML执行(Java Script)标记
将您的脚本元素替换为具有类属性class =“javascript”的div,并使用</div>
不要更改要执行的内容(以前是在脚本标记中,现在是在div标记中)
在页面中添加样式...
<style type="text/css"> .javascript { display: none; } </style>
现在使用jquery运行eval(应该已经包含Jquery js)
$('.javascript').each(function() {
eval($(this).text());
});`
您可以在我的博客上探索更多here。