Выполнение элементов ‹script›, вставленных с помощью .innerHTML

У меня есть сценарий, который вставляет некоторый контент в элемент с помощью innerHTML.

Например, контент может быть:

<script type="text/javascript">alert('test');</script>
<strong>test</strong>

Проблема в том, что код внутри тега <script> не выполняется. Я немного погуглил, но очевидных решений не было. Если я вставлял контент с помощью jQuery $(element).append(content);, части скрипта получали eval перед вставкой в ​​DOM.

У кого-нибудь есть фрагмент кода, который выполняет все <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>

person phidah    schedule 07.04.2010    source источник
comment
Вы пробовали добавлять контент (JS как innerHTML узла DOM), а затем вызывать добавленные функции? Например, если вы добавляете Javascript, содержащий функцию FOO () {}, вы можете попробовать вызвать функцию позже.   -  person Andreas    schedule 07.04.2010
comment
Я не думаю, что у вас может быть исполнение после вставки в DOM.   -  person Andreas    schedule 07.04.2010
comment
Почему вы не можете просто перебрать дочерние элементы элемента и для каждого из них, являющегося элементом скрипта, просто eval () использовать innerHtml этого дочернего элемента? Вот как я видел, как это делают крупные поставщики компонентов: каждый раз, когда они выполняют обратный вызов ajax, который добавляет что-то в DOM, они делают именно это. Имейте в виду, что это может быть медленным, особенно в IE7.   -  person slugster    schedule 07.04.2010
comment
Андреас: Если я добавляю функцию, например function testFunction(){ alert('test'); }, в код, вставленный в innerHTML, а затем пытаюсь вызвать ее, это говорит о том, что функция не определена.   -  person phidah    schedule 07.04.2010
comment
Здравствуйте, я опубликовал ответ, который может быть вам полезен.   -  person Andreas    schedule 07.04.2010
comment
Потрясающая фида, работает как шарм, ура   -  person Marcin    schedule 14.10.2012
comment
Я думаю, что абсолютно важно понимать, что такое поведение обозревателя предназначено для предотвращения атак с использованием межсайтовых сценариев. Если текст, который вы установили как innerHTML, предоставляется Бобом, он будет выполняться в браузере Алисы, вызывая повреждение (подумайте о форуме, где люди могут писать комментарии, добавляя к ним теги сценариев). Подробнее об этом можно прочитать здесь: en.wikipedia.org/wiki/Cross-site_scripting. Оставайся экономным!   -  person Xatian    schedule 03.05.2017
comment
HTML сильно изменился с 2010 года. В наши дни, возможно, вам захочется посмотреть: stackoverflow.com/a/58862506/890357   -  person marciowb    schedule 14.11.2019
comment
Потому что узел уже загружен. Разве вы не можете просто поместить атрибут deferred в свой тег скрипта?   -  person DylanYoung    schedule 02.04.2020


Ответы (21)


Сценарий 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]);
  }
};
person Larry K    schedule 14.07.2010
comment
Лучше используйте jQuery $(parent).html(code) - см. Мой ответ ниже. - person iirekm; 07.05.2014
comment
после того, как скрипт внедрен в DOM, как мне его удалить? - person S4beR; 31.10.2014
comment
Скрипт не рекурсивен, поэтому он будет смотреть только на прямых потомков. У меня работает: if (nodeName(child, "script" ) && (!child.type || child.type.toLowerCase() === "text/javascript")) { scripts.push(child); } else { exec_body_scripts(child); } - person st4wik; 10.02.2015
comment
Обратите внимание, что приведенный выше код не выполняет сценарии, загружаемые через src. Приведенный выше сценарий можно изменить для проверки elem.src и условной установки свойства src созданного элемента сценария вместо установки его текстового содержимого. - person Ryan Morlok; 09.11.2015
comment
Почему бы вместо этого не использовать глобальный eval трюк создания <script> элемента и вставки его в <head>? Оба они выполняют код JS, не раскрывая текущее закрытие. - person Finesse; 30.03.2017

Упрощенная версия ответа @joshcomley для ES6 с примером.

Без JQuery, без библиотеки, без eval, без изменения DOM, только чистый Javascript.

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);
  });
}

использование

$0.innerHTML = HTML;    // does *NOT* run <script> tags in HTML
setInnerHTML($0, HTML); // does run <script> tags in HTML
person allenhwkim    schedule 03.12.2017
comment
опечатка: имя функции setInnerHtml, а не setInnerHTML - person pery mimon; 12.08.2018
comment
Обратите внимание, что в связанном plnkr имя функции setInnerHTML, но она вызывается setInnerHtml из функции runB. Поэтому пример не работает - person danbars; 01.08.2019
comment
Рабочий пример plnkr.co/edit/b54rQgoSkguOFpIJ - person Vizor; 10.02.2021
comment
У меня проблема с VUE и v-html, где я хочу вставить HTML со скриптами ... это поможет мне решить мою проблему. stackoverflow.com/questions/68042540/ - person MiBol; 21.06.2021

@phidah ... Вот очень интересное решение вашей проблемы: http://24ways.org/2005/have-your-dom-and-script-it-too

Вместо этого это будет выглядеть так:

<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />

person user447963    schedule 15.09.2010
comment
и даже лучше, нет необходимости иметь изображение с событием onerror, удобно для быстрой XSS-инъекции j .com / blog / 47 / :) - person baptx; 30.06.2012
comment
Вы можете использовать <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');">, если хотите предотвратить бесполезный HTTP-запрос. - person Savas Vedova; 14.04.2015
comment
любить это ! (добавлено style="display:none;), чтобы скрыть значок сломанного изображения - person kris; 26.04.2017
comment
На самом деле <style> лучше, чем <img>, потому что он не делает сетевых запросов - person basin; 22.03.2019

Вот более короткий и эффективный сценарий, который также работает для сценариев со свойством 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 может вызвать уязвимость системы безопасности при неправильном использовании, это намного быстрее, чем создание тега сценария на лету.

person DividedByZero    schedule 03.11.2014
comment
это помогло мне, но я чувствую себя грязным, используя eval. Убедившись, что текст не может быть скомпрометирован, я не вижу уязвимости. - person John; 30.12.2014
comment
@ random-user eval был разработан, чтобы навредить пользователям. Выполнение любого динамического сценария представляет собой риск, и поэтому CSP называет его 'unsafe-eval', потому что это так. Вы также ставите под угрозу безопасность своих сайтов, если используете его в библиотеке, поскольку они не могут его отключить. - person jonathanKingston; 22.08.2015
comment
Тестирование этого в Chrome 44 вызывает бесконечный цикл при вызове appendChild, поскольку это увеличивает значение scripts.length. - person Codewithcheese; 11.10.2015
comment
Сценарии со свойством src будут загружаться асинхронно и выполняться по мере поступления. Порядок не сохраняется. Встроенные скрипты также будут выполняться вне очереди, синхронно перед асинхронными. - person robert4; 12.02.2016
comment
новый вызов: [...document.querySelectorAll(`#${id} script`)].forEach(script => { if (scripts.src != "") { ... }}) - person mplungjan; 12.11.2020

Вы должны использовать не свойство innerHTML, а метод appendChild узла: узел в дереве документа [HTML DOM]. Таким образом, вы сможете позже вызвать свой внедренный код.

Убедитесь, что вы понимаете, что node.innerHTML не то же самое, что node.appendChild. Возможно, вы захотите потратить некоторое время на Справочник клиента Javascript для получения более подробной информации и DOM. Надеюсь, следующее поможет ...

Образцы инъекционных работ:

<!DOCTYPE HTML>
<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>

С уважением

person Andreas    schedule 07.04.2010
comment
Наконец, кто-то, кто на самом деле немного объясняет проблему, а не все другие ответы try this, look how clever I am. Заслуживает УФ-излучения, получил мое. - person RiggsFolly; 07.02.2016
comment
Уф, потому что это самый простой способ ввести код javascript, который выполняется после инъекции. Я только не понимаю разницы между добавлением с помощью innerHTML, которое не выполняется, и описанным выше способом с помощью appendChild, который выполняется. Я успешно использовал это для создания динамической страницы со скриптом с нуля с помощью socket.io - person wetlip; 26.12.2016
comment
var _in = document.getElementById(inject);, думаю. - person Ron Burk; 16.12.2017

Попробуйте этот фрагмент:

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);
person fantactuka    schedule 03.06.2010
comment
да, этот метод работает, но вы получите ошибки, если у вас есть комментарии или console.logs, поэтому следите за этим, также вы можете изменить учетные записи для модулей var modules = [] var cleaned = text.replace (/ ‹script ([^› ] *) ›([\ S \ S] *?)‹ \ / Script ›/ gi, function (m, tags, script) {if (/type=module/.test(tags)) {modules.push (script ) return} скрипты + = скрипт + '\ n' return ''}) - person zavr; 13.02.2019

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 в моем проекте

person Bruce    schedule 22.01.2013
comment
Быстро и красиво. Спасибо. - person Jorge Fuentes González; 04.12.2014
comment
Вот и все! Спасибо. - person Floris; 11.12.2017
comment
Решил мою проблему после 4 часов проб и ошибок (кстати, используя Springboot и Thymeleaf, и это все еще работает). - person todbott; 14.07.2021

Решение без использования eval:

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);
  }
}

По сути, это клонирует тег сценария, а затем заменяет заблокированный тег сценария новым сгенерированным, что обеспечивает выполнение.

person joshcomley    schedule 06.03.2017

scriptNode.innerHTML = code не работал в IE. Единственное, что нужно сделать, это заменить на scriptNode.text = code, и он работает нормально

person Jorge    schedule 16.09.2011

Легче использовать 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.

person iirekm    schedule 07.05.2014
comment
Что заставляет вас говорить, что это проще? Это даже не короче. Я всегда считаю, что чрезмерное использование jQuery - плохая идея. - person Manngo; 21.10.2019

Просто делать:

document.body.innerHTML = document.body.innerHTML + '<img src="../images/loaded.gif" alt="" onload="alert(\'test\');this.parentNode.removeChild(this);" />';
person Lambder    schedule 03.10.2014
comment
похоже на гениальную идею - person pery mimon; 13.08.2018

Продолжение от Ларри. Я сделал рекурсивный поиск по всему блоку и дочерним узлам.
Теперь скрипт также будет вызывать внешние скрипты, указанные в параметре src. Скрипты добавляются к заголовку, а не вставляются и размещаются в том порядке, в котором они были найдены. Так что специально сохраняются скрипты заказа. И каждый скрипт выполняется синхронно, подобно тому, как браузер обрабатывает начальную загрузку DOM. Итак, если у вас есть блок сценария, который вызывает jQuery из CDN, а следующий узел сценария использует jQuery ... Нет проблем! Да, и я пометил добавленные сценарии сериализованным идентификатором, основанным на том, что вы установили в параметре тега, чтобы вы могли найти, что было добавлено этим сценарием.

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);
}
person BadOPCode    schedule 23.07.2014

Выполнить элемент сценария с помощью обычного .innerHTML, просто не помещайте начальный и закрывающий теги в значение .innerhtml. Взгляните на мой код, это просто, как обычный код, без jQuery или любой другой длинной функции JavaScript

<h2>Click on Run to execute script</h2>
<button type="button" onclick="run()">Run</button>
<script id="my_script"></script>
<script>
document.getElementById("my_script").innerHTML= "function run(){alert('Wow! Script executed :)');}";

</script>

person Rohit kumar    schedule 26.05.2021

Вы можете просмотреть этот пост. Код может выглядеть так:

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]);
}
person Darin Dimitrov    schedule 07.04.2010

Благодаря сценарию Ларри, который отлично работал в IE10, я использовал вот что:

$('#' + id)[0].innerHTML = result;
$('#' + id + " script").each(function() { this.text = this.text || $(this).text();} );
person AxD    schedule 25.06.2013

Попробуйте это, у меня это работает в 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

Для запуска сценария он должен быть создан как узел, а затем добавлен как дочерний. Вы даже можете добавить скрипт внутри ранее введенного 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"
person Trev14    schedule 27.07.2017

Вот мое решение в недавнем проекте.

<!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>

person Gray    schedule 12.02.2018

Расширяя ответ Lambder

document.body.innerHTML = '<img src="../images/loaded.gif" alt="" > onload="alert(\'test\');this.parentNode.removeChild(this);" />';

Вы можете использовать образ base64 для создания и загрузки вашего скрипта

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2P4//8/AwAI/AL+iF8G4AAAAABJRU5ErkJggg=="
    onload="var script = document.createElement('script');  script.src = './yourCustomScript.js'; parentElement.append(script);" />

Или, если у вас есть Iframe, вы можете использовать его вместо

<iframe src='//your-orginal-page.com' style='width:100%;height:100%'
    onload="var script = document.createElement('script');  script.src = './your-coustom-script.js'; parentElement.append(script);"
    frameborder='0'></iframe>
person pery mimon    schedule 13.08.2018

Мне нужно было что-то подобное, но мне нужно, чтобы сценарий оставался или воссоздавался в том же месте, что и исходный сценарий, поскольку мой сценарий нацелен на расположение тега сценария в DOM для создания / целевых элементов. Я также сделал сценарий рекурсивным, чтобы убедиться, что он также работает, если он находится более чем на один уровень ниже.

ПРИМЕЧАНИЕ. Я использую здесь const, если у вас более старый браузер, просто используйте var.

    window.exec_body_scripts = function(body_el) {
        // ref: https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml based on Larry K's answer
        // 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.
        const
            type__Js = 'text/javascript',
            tagName__Script = 'script',
            tagName__Script__Upper = tagName__Script.toUpperCase();
        var scripts = [], script, i;
        function evalScript(elem) {
            var parent = elem.parentNode,
                data = (elem.text || elem.textContent || elem.innerHTML || ""),
                script = document.createElement(tagName__Script);

            script.type = type__Js;
            try {
                // doesn't work on ie...
                script.appendChild(document.createTextNode(data));
            } catch (e) {
                // IE has funky script nodes
                script.text = data;
            }
            // Make sure to re-insert the script at the same position
            // to make sure scripts that target their position
            // in the DOM function as expected.
            var parent = elem.parentNode;
            parent.insertBefore(script, elem);
            parent.removeChild(elem);
        };
        // Get all scripts (recursive)
        if (typeof (document.querySelectorAll) !== typeof (void 0)) {
            document.querySelectorAll('script').forEach((scr) => { if (!scr.type || scr.type.toLowerCase() === type__Js) scripts.push(scr); });
        }
        else {
            var children_nodes = body_el.childNodes, child;
            for (i = 0; children_nodes[i]; i++) {
                child = children_nodes[i];
                if (
                    child.nodeName
                    &&
                    child.nodeName.toUpperCase() === tagName__Script__Upper
                    &&
                    (
                        !child.type
                        ||
                        child.type.toLowerCase() === type__Js
                    )
                ) {
                    scripts.push(child);
                }
                // Recursive call
                window.exec_body_scripts(child);
            }
        }
        for (i = 0; scripts[i]; i++) {
            evalScript(scripts[i]);
        }
    };
person NKCSS    schedule 19.01.2019

Сделал эту новую вспомогательную функцию на TypeScript, может, кто-то это оценит. Если вы удалите объявление типа из параметра скрипта, это будет просто JS.

const evalPageScripts = () => {
  const scripts = document.querySelectorAll('script');

  scripts.forEach((script: HTMLScriptElement) => {
    const newScript = document.createElement('script');
    newScript.type = 'text/javascript';
    newScript.src = script.src;

    if (script.parentNode) {
      script.parentNode.removeChild(script);
    }

    return document.body.appendChild(newScript);
  })
};

export default evalPageScripts;

person kvba    schedule 15.07.2020

Попробуйте функцию eval ().

data.newScript = '<script type="text/javascript">//my script...</script>'
var element = document.getElementById('elementToRefresh');
element.innerHTML = data.newScript;
eval(element.firstChild.innerHTML);

Это реальный пример из проекта, который я разрабатываю. Благодаря этому сообщению

person IgniteCoders    schedule 08.07.2014