2009-07-29 41 views
154

Intenté cargar algunos guiones en una página usando innerHTML en un <div>. Parece que el script se carga en el DOM, pero nunca se ejecuta (al menos en Firefox y Chrome). ¿Hay alguna manera de ejecutar scripts al insertarlos con innerHTML?¿Se pueden insertar scripts con innerHTML?

Código de ejemplo:

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

Respuesta

66

Usted tiene que usar eval() para ejecutar cualquier código de script que ha insertado como texto DOM.

MooTools hará esto automáticamente, y estoy seguro de que jQuery también (dependiendo de la versión. JQuery versión 1.6+ usa eval). Esto ahorra una gran cantidad de problemas al analizar las etiquetas <script> y escapar de su contenido, así como un montón de otros "errores".

En general, si usted va a eval() usted mismo, que quieren crear/enviar el código de script sin ningún marcado HTML como <script>, ya que estos no se eval() correctamente.

+7

Lo que realmente quiero hacer es cargar un script externo, no sólo eval una secuencia de comandos local. Agregar una etiqueta de script con innerHTML es mucho más breve que crear un elemento DOM de script y agregarlo al cuerpo, y estoy tratando de hacer que mi código sea lo más breve posible. ¿Tienes que crear los elementos del guión dom y agregarlos a la dom en lugar de usar algo como innerHTML? ¿Hay alguna manera de hacer esto con document.write desde dentro de una función? – Craig

+5

Como sugiere zombat, utilice un marco de JavaScript para cargar el script externo, no intente reinventarlo. JQuery lo hace extremadamente fácil, solo incluya JQuery y llame a: $ .getScript (url). También puede proporcionar una función de devolución de llamada que se ejecutará una vez que se carga el script. –

+2

Ariel tiene razón. Agradezco tratar de mantener el código corto, y agregar una etiqueta '

0

Sí, puedes, pero tienes que hacerlo fuera del DOM y el orden debe ser el correcto.

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>'; 
window.onload = function(){ 
    var n = document.createElement("div"); 
    n.innerHTML = scr; 
    document.body.appendChild(n); 
} 

... alertará 'foo'. Esto no funcionará:

document.getElementById("myDiv").innerHTML = scr; 

Y aunque esto no funcionará, debido a que el nodo se inserta en primer lugar:

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>'; 
window.onload = function(){ 
    var n = document.createElement("div"); 
    document.body.appendChild(n); 
    n.innerHTML = scr; 
} 
+15

Por lo que vale: esto no parece funcionar en los navegadores actuales. –

+0

Lo que en realidad es algo bueno, creo. – jayarjo

66

Aquí es una solución muy interesante para su problema: http://24ways.org/2005/have-your-dom-and-script-it-too

Así que use esto en lugar de etiquetas de scripts:

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

+2

¡muy bueno, y trabajando completamente en todos los navegadores! –

+10

¡Esto es brillante! – confile

+0

No funciona para mí, cuando se inserta en un resultado de una solicitud de AJAX: falta de sintaxis; declaración anterior al inicio de la secuencia de comandos – Oliver

33

Puede crear secuencias de comandos y luego inyectar el contenido.

var g = document.createElement('script'); 
var s = document.getElementsByTagName('script')[0]; 
g.text = "alert(\"hi\");" 
s.parentNode.insertBefore(g, s); 

Esto funciona en todos los navegadores :)

+0

A menos que no haya otros elementos de script en el documento. Use 'document.documentElement' en su lugar. –

+3

No es necesario porque está escribiendo un script desde otro script. '' –

+2

Quién dice que es de otro guión? Puede ejecutar JavaScript sin los elementos '

-1

Ejecutar etiqueta (Java Script) de innerHTML

Reemplazar el elemento de guión con div que tiene un atributo de clase class = "javascript" y cerrarla con </div>

No cambie el contenido que desea ejecutar (anteriormente estaba en la etiqueta de script y ahora está en la etiqueta div)

Agrega un estilo a tu página ...

<style type="text/css"> .javascript { display: none; } </style>

Ahora ejecute eval usando jQuery (JS Jquery deben estar ya incluidos)

$('.javascript').each(function() { 
     eval($(this).text()); 

    });` 

se puede explorar más here, en mi blog.

23

que utiliza este código, que está trabajando muy bien

var arr = MyDiv.getElementsByTagName('script') 
for (var n = 0; n < arr.length; n++) 
    eval(arr[n].innerHTML)//run script inside div 
+0

Gracias. Resolvió mi problema de agregar el código de Disqus Universal a una ventana emergente modal creada con el complemento Jquery de TinyBox2. – gsinha

+3

Desafortunadamente, esta solución no funciona cuando el script contiene funciones que se invocarán más adelante. –

-4

Probé esto y que está funcionando. Gracias @mwilcox.

var maint = document.getElementById("maint"); 
maint.innerHTML = ''; 
var inner = '<form >'+ 
    'add new member:-<br>'+ 
    '<input type="text" name="namea" placeholder="enter ur name">'+ 
    '</form >'; 

maint.innerHTML = inner; 
49

Aquí es un método que sustituye a todos los scripts de forma recursiva con los ejecutables:

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

Ejemplo de llamada:

nodeScriptReplace(document.getElementsByTagName("body")[0]); 
+4

Estoy un poco sorprendido de que tu respuesta haya bajado. En mi humilde opinión, esta es la mejor solución, este método incluso le permite restringir las secuencias de comandos con URL específicas o contenido. – davidmh

+0

ahora funciona en firefox – inf3rno

+0

@ inf3rno does or does not? Solía ​​funcionar, ¿alguien alguna vez afirmó algo diferente? – momomo

1

Krasimir Tsonev tiene una gran solución que superar todos los problemas. Su método no necesita el uso de eval, por lo que no existen problemas de rendimiento ni de seguridad. Le permite establecer innerHTML cadena contiene html con js y traducirlo de inmediato a un elemento DOM, mientras que también ejecuta las partes js existen a lo largo del código. corto, simple y funciona exactamente como lo desea.

disfrutar de su solución:

http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element

Notas importantes:

  1. Es necesario utilizar con el elemento objetivo con la etiqueta div
  2. Es necesario para envolver la cadena src con la etiqueta div.
  3. Si escribe la cadena src directamente e incluye partes js, preste atención para escribir las etiquetas de script de cierre correctamente (con \ before /) ya que esta es una cadena.
1

Use $(parent).html(code) en lugar de parent.innerHTML = code.

Lo siguiente también corrige scripts que usan document.write y scripts cargados a través del atributo src. Lamentablemente, incluso esto no funciona con las secuencias de comandos de 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 
    }) 
} 

Source

+1

un poco tarde aquí, pero cualquiera que pueda estar usando este método, observe que en JQuery necesita cargar sus scripts usando $ .loadScript (url) en lugar de

-3

añada la etiqueta en la cabeza después de poner texto en innerHTML y se ejecutará

4

Para cualquier persona todavía está tratando de hacer esto, no, no se puede inyectar un script utilizando innerHTML, pero es posible cargar una cadena en una etiqueta de script usando Blob y URL.createObjectURL.

He creado un ejemplo que le permite ejecutar una cadena como una secuencia de comandos y obtener las exportaciones '' de la secuencia de comandos devuelven a través de una promesa:

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

He simplificado esto desde mi aplicación real, así que no hay promesas de que no haya ningún error en ello. Pero el principio funciona.

Si no le importa recuperar ningún valor después de que se ejecuta el script, es aún más fácil; simplemente deje fuera los bits Promise y onload. Ni siquiera necesita envolver el script o crear la propiedad global window.__load_script_exports_.

+1

Lo probé y funciona en Chrome 57. innerHTML en una etiqueta de script ejecuta el texto. – iPherian

+0

Es interesante, no funcionó antes. Me pregunto si este comportamiento es de navegador cruzado o solo en cromo 57. – JayArby

0

Aquí es una función recursiva para establecer el innerHTML de un elemento que yo uso en nuestro servidor de anuncios:

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

Se puede ver la demo here.

-1

Mi solución para este problema es establecer un Mutation Observer para detectar <script></script> nodos y luego reemplazarlo con un nuevo nodo <script></script> con el mismo src. Por ejemplo:

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

Pruebe usar template y document.importNode. He aquí un ejemplo:

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

Cuestiones relacionadas