Можно ли вставлять скрипты с помощью innerHTML?
Я попытался загрузить некоторые скрипты на страницу с помощью 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>
14 ответов:
вы должны использовать eval () для выполнения любого кода скрипта, который вы вставили в качестве текста DOM.
MooTools сделает это для вас автоматически, и я уверен, что jQuery также будет (в зависимости от версии. jQuery версии 1.6+ использует
eval
). Это экономит много хлопот разбора<script>
теги и экранирование вашего контента, а также куча других "gotchas".вообще, если вы собираетесь
eval()
это вы сами, хотите создать / отправить скрипт код без какой-либо разметки HTML, такие как<script>
, а неeval()
правильно.
вот очень интересное решение вашей проблемы: http://24ways.org/2005/have-your-dom-and-script-it-too
поэтому используйте это вместо тегов скрипта:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
вот метод, который рекурсивно заменяет все скрипты исполняемыми:
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]);
вы можете создать скрипт, а затем ввести содержимое.
var g = document.createElement('script'); var s = document.getElementsByTagName('script')[0]; g.text = "alert(\"hi\");" s.parentNode.insertBefore(g, s);
это работает во всех браузерах :)
я использовал этот код, он работает нормально
var arr = MyDiv.getElementsByTagName('script') for (var n = 0; n < arr.length; n++) eval(arr[n].innerHTML)//run script inside div
для тех, кто все еще пытается это сделать, нет, вы не можете ввести скрипт с помощью
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) }); }
я упростил это из моей фактической реализации, поэтому никаких обещаний, что в нем нет никаких ошибок. Но принцип работает.
если вы не заботьтесь о том, чтобы вернуть какое-либо значение после запуска скрипта, это еще проще; просто оставьте
Promise
иonload
бит. Вам даже не нужно обернуть скрипт или создать глобальныйwindow.__load_script_exports_
собственность.
Красимир Цонев имеет отличное решение, которое преодолевает все проблемы. Его метод не нуждается в использовании eval, поэтому никаких проблем с производительностью или безопасностью не существует. Это позволяет вам установить строку innerHTML, содержащую html с js, и немедленно перевести ее в элемент DOM, а также выполнить части js, существующие вдоль кода. короткий ,простой, и работает именно так, как вы хотите.
наслаждайтесь его решение:
http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element
важное замечание:
- вам нужно обернуть целевой элемент с тегом div
- вам нужно обернуть строку src с тегом div.
- если вы пишете строку src напрямую, и она включает в себя части js, обратите внимание, чтобы правильно написать теги сценария закрытия (с \ before/), поскольку это строка.
использовать
$(parent).html(code)
вместоparent.innerHTML = code
.ниже также исправлены скрипты, которые используют
document.write
и скрипты загружаются через . К сожалению, даже это не работает со скриптами 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 }) }
попробуйте использовать шаблон и документ.этого importnode. Вот пример:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Sample</title> </head> <body> <h1 id="hello_world">Sample</h1> <script type="text/javascript"> var div = document.createElement("div"); var t = document.createElement('template'); t.innerHTML = "Check Console tab for javascript output: Hello world!!!<br/><script type='text/javascript' >console.log('Hello world!!!');<\/script>"; for (var i=0; i < t.content.childNodes.length; i++){ var node = document.importNode(t.content.childNodes[i], true); div.appendChild(node); } document.body.appendChild(div); </script> </body> </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); }
...предупредит 'фу'. Это не сработает:
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; }
вот рекурсивная функция для установки 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); } }
вы можете увидеть демо здесь.
вы могли бы сделать это вот так:
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); }
выполнить (Java Script) тег из innerHTML
замените элемент скрипта на div с атрибутом класса class= "javascript" и закройте его с помощью
</div>
не изменяйте содержимое, которое вы хотите выполнить (ранее оно было в теге script, а теперь находится в теге div)
добавить стиль на Вашей странице...
<style type="text/css"> .javascript { display: none; } </style>
Теперь запустите eval с помощью jquery (Jquery js должен быть уже включен)
$('.javascript').each(function() { eval($(this).text()); });`
вы можете узнать больше здесь в моем блоге.
мое решение этой проблемы-установить Мутация Наблюдателя обнаруживает
<script></script>
узлы, а затем заменить его на новый<script></script>
узел с тем же src. Например: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});