Можно ли вставлять скрипты с помощью 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 168

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

важное замечание:

  1. вам нужно обернуть целевой элемент с тегом div
  2. вам нужно обернуть строку src с тегом div.
  3. если вы пишете строку 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});