webentwicklung-frage-antwort-db.com.de

Ausführen von <script> -Elementen, die mit .innerHTML eingefügt wurden

Ich habe ein Skript, das mit innerHTML Inhalte in ein Element einfügt.

Der Inhalt könnte zum Beispiel sein:

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

Das Problem ist, dass der Code im <script>-Tag nicht ausgeführt wird . Ich habe etwas gegoogelt, aber es gab keine offensichtlichen Lösungen. Wenn ich den Inhalt mit jQuery $(element).append(content); eingefügt habe, bekamen die Skriptteile eval 'd, bevor sie in das DOM eingefügt wurden.

Hat jemand einen Code-Ausschnitt, der alle <script>-Elemente ausführt? Der jQuery-Code war etwas komplex, daher konnte ich nicht wirklich herausfinden, wie er gemacht wurde.

Bearbeiten :

Durch einen Blick in den jQuery-Code habe ich herausgefunden, wie dies von jQuery geschieht, was zu folgendem Code führte:

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>
89
phidah

Das Skript des OP funktioniert nicht in IE 7. Mit Hilfe von SO können Sie Folgendes tun:

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]);
  }
};
22
Larry K

@phidah ... Hier ist eine sehr interessante Lösung für Ihr Problem: http://24ways.org/2005/have-your-dom-and-script-it-too

Also würde es stattdessen so aussehen:

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

28
user447963

Hier ist ein kürzeres, effizienteres Skript, das auch für Skripts mit der Eigenschaft src geeignet ist:

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

Hinweis: Während eval eine Sicherheitsanfälligkeit verursachen kann, wenn sie nicht ordnungsgemäß verwendet wird, ist sie viel schneller als das Erstellen eines Skript-Tags im laufenden Betrieb.

17
DividedByZero

Probieren Sie diesen Ausschnitt aus:

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);
15
fantactuka

Sie sollten nicht die innerHTML-Eigenschaft verwenden, sondern die appendChild-Methode des Knotens: einen Knoten in einer Dokumentstruktur [HTML DOM]. Auf diese Weise können Sie später Ihren injizierten Code aufrufen.

Stellen Sie sicher, dass Sie verstehen, dass node.innerHTML nicht mit node.appendChild identisch ist. Sie sollten einige Zeit mit der Javascript-Clientreferenz verbringen, um weitere Informationen und das DOM zu erhalten. Hoffe, das folgende hilft ...

Probeninjektionsarbeiten:

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

grüße,

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

Es funktioniert in Chrome in meinem Projekt

8
Bruce

Eine Lösung ohne "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);
  }
}

Dadurch wird das Skript-Tag im Wesentlichen geklont und anschließend das blockierte Skript-Tag durch das neu generierte Tag ersetzt, sodass die Ausführung möglich ist.

7
joshcomley

Vereinfachte ES6-Version der Antwort von @ joshcomley mit einem Beispiel.

Keine JQuery, keine Bibliothek, keine Bewertung, keine DOM-Änderung, nur reines 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);
  });
}

Verwendungszweck

$0.innerHTML = HTML;    // does *NOT* run <script> tags in HTML
setInnerHTML($0, HTML); // does run <script> tags in HTML
5
allenhwkim

Es ist einfacher, jquery $(parent).html(code) anstelle von parent.innerHTML = code zu verwenden:

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

Dies funktioniert auch bei Skripts, die document.write verwenden, und Skripts, die über das Attribut src geladen werden. Leider funktioniert dies auch nicht mit Google AdSense-Skripts.

3
iirekm

scriptNode.innerHTML = code hat für IE nicht funktioniert. Das einzige, was zu tun ist, ist durch scriptNode.text = code zu ersetzen und es funktioniert gut

3
Jorge

Mach einfach:

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

Abhängig von der Antwort von Lambder

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

Sie können base64-Image verwenden, um Ihr Skript zu erstellen und zu laden

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

Oder wenn Sie eine Iframe haben, können Sie sie stattdessen verwenden

<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>
0
pery mimon

Dank Larrys Skript, das in IE10 perfekt funktioniert hat, habe ich Folgendes verwendet:

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

Probieren Sie es aus, es funktioniert für mich auf Chrome, Safari und Firefox:

var script = document.createElement('script');
script.innerHTML = 'console.log("hi")';
document.body.appendChild(script); 
--> logs "hi"

Beachten Sie jedoch, dass das folgende div-nested-Skript NICHT ausgeführt wird:

var script = document.createElement('div');
script.innerHTML = '<script>console.log("hi")</script>';
document.body.appendChild(script);
--> doesn't log anything

Damit ein Skript ausgeführt werden kann, muss es als Knoten erstellt und als untergeordnetes Element angehängt werden. Sie können sogar ein Skript in ein zuvor eingefügtes div einfügen und es wird ausgeführt (Ich habe das schon einmal gemacht, als ich versuchte, Ad-Server-Code zum Laufen zu bringen):

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"
0
Trev14

Ich brauchte etwas Ähnliches, musste aber das Skript an der gleichen Stelle wie das ursprüngliche Skript bleiben oder neu erstellt werden, da mein Skript die Position des Skript-Tags im DOM zum Erstellen von/Zielelementen anvisiert. Ich habe das Skript auch rekursiv erstellt, um sicherzustellen, dass es auch funktioniert, wenn es mehr als eine Stufe tiefer liegt.

HINWEIS: Ich verwende const hier. Wenn Sie einen älteren Browser haben, verwenden Sie einfach 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]);
        }
    };
0
NKCSS

Sie können einen Blick auf diesen Beitrag werfen. Der Code könnte so aussehen:

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]);
}
0
Darin Dimitrov

Versuchen Sie die Funktion eval ().

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

Dies ist ein echtes Beispiel aus einem Projekt, das ich entwickle ... Dank diesem post

0
IgniteCoders

Ausfahren von Larry. Ich ließ es rekursiv den gesamten Block und die untergeordneten Knoten durchsuchen.
Das Skript ruft jetzt auch externe Skripts auf, die mit dem Parameter src .. angegeben sind. Skripts werden an den Kopf angehängt, anstatt eingefügt und in der Reihenfolge angeordnet, in der sie gefunden wurden. So werden speziell Auftragsskripte erhalten. Jedes Skript wird synchron ausgeführt, ähnlich wie der Browser das anfängliche Laden des DOM ausführt. Wenn Sie also einen Skriptblock haben, der jQuery von einem CDN aufruft und der nächste Skriptknoten jQuery verwendet ... Kein Problem! Oh, und ich habe die angehängten Skripte mit einer serialisierten ID versehen, die auf den von Ihnen im Tag-Parameter festgelegten Werten basiert, damit Sie herausfinden können, was von diesem Skript hinzugefügt wurde.

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