Ejecutando <script> inyectado por innerHTML después de la llamada AJAX

100

Hay un div llamado "Contenido":

<div id="content"></div>

Debe llenarse con datos de un archivo PHP, por AJAX, incluida una <script>etiqueta. Sin embargo, el script dentro de esta etiqueta no se está ejecutando.

<div id="content"><!-- After AJAX loads the stuff that goes here -->
   <script type="text/javascript">
     //code
   </script>
   <!-- More stuff that DOES work here -->
</div>
JCOC611
fuente
¿Cómo estás cargando el div? depende de la biblioteca que use, normalmente puede controlar si solo desea ejecutar los scripts después de la carga de ajax.
Bill Yang
1
Después de window.onloadcrear un XMTHttpRequestobjeto para solicitar otra página (php) que contenga el contenido del div, incluido un script. Estoy haciendo esto con JS simple, sin bibliotecas (que no sean las mías)
JCOC611

Respuestas:

65

JavaScript insertado como texto DOM no se ejecutará. Sin embargo, puede utilizar el patrón de secuencia de comandos dinámica para lograr su objetivo. La idea básica es mover el script que desea ejecutar a un archivo externo y crear una etiqueta de script cuando obtenga su respuesta Ajax. Luego establece el srcatributo de su etiqueta de script y listo, carga y ejecuta el script externo.

Esta otra publicación de StackOverflow también puede serle útil: ¿Se pueden insertar scripts con innerHTML? .

Chocula
fuente
puede seleccionar todos los scripts cargados y ejecutarlos mediante la función eval ():$('#audit-view script').each(function (index, element) { eval(element.innerHTML); })
Jerzy Gebler
El Javascript agregado a la página debería ejecutarse. ( jsfiddle.net/wnn5fz3m/1 por ejemplo). La pregunta es ¿por qué a veces no?
NoBugs
@Chocula moví mi script a un archivo separado, pero aún así no funciona de forma parcial, ¿pueden ayudarme con esto? stackoverflow.com/questions/42941856/…
Samra
FYI (@NoBugs me da una pista). Intenté simple javascirpt element.innerHTML = zzz y desnt work. Luego probé Jquery $ (element) .html (zzz) y funcioné.
gtryonp
66

Usé este código, está funcionando bien

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div
Firas Nizam
fuente
4
Si me preguntas, esta es una respuesta mucho mejor que la aceptada. Esto es más o menos una inyección de JavaScript.
Xedret
Si bien es útil, esto realmente no responde a la pregunta de por qué no se ejecutarían los scripts . Vea, por ejemplo, jsfiddle.net/fkqmcaz7 y jsfiddle.net/wnn5fz3m/1 Absolutamente debería ejecutarse, y no encuentro un caso simplificado de dónde no funciona.
NoBugs
1
@NaoiseGolden OP está preguntando literalmente sobre la ejecución del script, así que creo que está bien aquí. :-)
cambia el
1
Eval () no es muy recomendable. Vea esto - stackoverflow.com/questions/9107847/…
Kings
15

Si carga un bloque de script dentro de su div a través de Ajax como este:

<div id="content">
    <script type="text/javascript">
    function myFunction() {
      //do something
    }
    myFunction();
    </script>
</div>

... simplemente actualiza el DOM de su página, myFunction () no necesariamente se llama.

Puede usar un método de devolución de llamada Ajax como el del método ajax () de jQuery para definir qué ejecutar cuando finalice la solicitud.

Lo que está haciendo es diferente a cargar una página con JavaScript incluido desde el principio (que se ejecuta).

Un ejemplo de cómo utilizar la devolución de llamada exitosa y la devolución de llamada de error después de obtener algún contenido:

  $.ajax({
    type: 'GET',
    url: 'response.php',
    timeout: 2000,
    success: function(data) {
      $("#content").html(data);
      myFunction();
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) {
      alert("error retrieving content");
    }

Otra manera rápida y sucia es usar eval () para ejecutar cualquier código de script que haya insertado como texto DOM si no desea usar jQuery u otra biblioteca.

Christopher Tokar
fuente
usando la devolución de llamada de jQuery, ¿cómo podría "ejecutar" el código dentro del nuevo <script>?
JCOC611
Agregué un ejemplo del uso de la devolución de llamada exitosa:
Christopher Tokar
1
Muchas gracias! el "myFunction ();" parte era lo que estaba dejando fuera de mi código.
Jason
11

Aquí está el script que evaluará todas las etiquetas del script en el texto.

function evalJSFromHtml(html) {
  var newElement = document.createElement('div');
  newElement.innerHTML = html;

  var scripts = newElement.getElementsByTagName("script");
  for (var i = 0; i < scripts.length; ++i) {
    var script = scripts[i];
    eval(script.innerHTML);
  }
}

Simplemente llame a esta función después de recibir su HTML del servidor. Tenga cuidado: el uso evalpuede ser peligroso.

Demostración: http://plnkr.co/edit/LA7OPkRfAtgOhwcAnLrl?p=preview

Evgenii
fuente
5

Esto 'simplemente funciona' para mí usando jQuery, siempre que no intente agregar un subconjunto del HTML devuelto por XHR al documento. (Vea este informe de errores que muestra el problema con jQuery).

Aquí hay un ejemplo que muestra cómo funciona:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html lang="en"> 
<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
    <title>test_1.4</title> 
    <script type="text/javascript" charset="utf-8" src="jquery.1.4.2.js"></script> 
    <script type="text/javascript" charset="utf-8"> 
        var snippet = "<div><span id='a'>JS did not run<\/span><script type='text/javascript'>" +
        "$('#a').html('Hooray! JS ran!');" +
        "<\/script><\/div>";
        $(function(){
            $('#replaceable').replaceWith($(snippet));
        });
    </script> 
</head> 
<body> 
    <div id="replaceable">I'm going away.</div> 
</body> 
</html>

Aquí está el equivalente de lo anterior: http://jsfiddle.net/2CTLH/

Phrogz
fuente
1
Es muy poco probable que esta sea la solución al problema actualmente, ya que era un error con una versión realmente antigua de jQuery.
NoBugs
3

Aquí hay una función que puede usar para analizar las respuestas AJAX, especialmente si usa minifiedjs y desea que ejecute el Javascript devuelto o simplemente desea analizar los scripts sin agregarlos al DOM, también maneja errores de excepción. Usé este código en la biblioteca php4sack y es útil fuera de la biblioteca.

function parseScript(_source) {
    var source = _source;
    var scripts = new Array();

    // Strip out tags
    while(source.toLowerCase().indexOf("<script") > -1 || source.toLowerCase().indexOf("</script") > -1) {
        var s = source.toLowerCase().indexOf("<script");
        var s_e = source.indexOf(">", s);
        var e = source.toLowerCase().indexOf("</script", s);
        var e_e = source.indexOf(">", e);

        // Add to scripts array
        scripts.push(source.substring(s_e+1, e));
        // Strip from source
        source = source.substring(0, s) + source.substring(e_e+1);
    }

    // Loop through every script collected and eval it
    for(var i=0; i<scripts.length; i++) {
        try {
          if (scripts[i] != '')
          {         
            try  {          //IE
                  execScript(scripts[i]);   
      }
      catch(ex)           //Firefox
      {
        window.eval(scripts[i]);
      }   

            }  
        }
        catch(e) {
            // do what you want here when a script fails
         // window.alert('Script failed to run - '+scripts[i]);
          if (e instanceof SyntaxError) console.log (e.message+' - '+scripts[i]);
                    }
    }
// Return the cleaned source
    return source;
 }
Andre Van Zuydam
fuente
2

Si está inyectando algo que necesita la etiqueta de secuencia de comandos, es posible que obtenga un error de sintaxis no detectado y diga token ilegal . Para evitar esto, asegúrese de escapar de las barras diagonales en sus etiquetas de script de cierre. es decir;

var output += '<\/script>';

Lo mismo ocurre con cualquier etiqueta de cierre, como una etiqueta de formulario.

TommyRay
fuente
0

Tenía una publicación similar aquí, addEventListener load on ajax load SIN jquery

La forma en que lo resolví fue insertar llamadas a funciones dentro de mi función stateChange. La página que configuré tenía 3 botones que cargarían 3 páginas diferentes en contentArea. Como tenía que saber qué botón se estaba presionando para cargar la página 1, 2 o 3, podría usar fácilmente las declaraciones if / else para determinar qué página se está cargando y luego qué función ejecutar. Lo que estaba tratando de hacer era registrar diferentes oyentes de botones que solo funcionarían cuando se cargó la página específica debido a los ID de los elementos.

entonces...

si (se está cargando la página1, carga de la página = 1) ejecutar la función registerListeners1

luego lo mismo para la página 2 o 3.

Richard Chase
fuente
0

Esto funcionó para mí llamando a eval en cada contenido de script desde ajax .done:

$.ajax({}).done(function (data) {      
    $('div#content script').each(function (index, element) { eval(element.innerHTML); 
})  

Nota: no escribí parámetros en $ .ajax que debes ajustar de acuerdo con tu ajax.

shivgre
fuente
0

Mi conclusión es que HTML no permite etiquetas NESTED SCRIPT. Si está utilizando javascript para inyectar código HTML que incluye etiquetas de script en su interior, no funcionará porque el javascript también va en una etiqueta de script. Puedes probarlo con el siguiente código y verás que no va a funcionar. El caso de uso es que está llamando a un servicio con AJAX o similar, está obteniendo HTML y desea inyectarlo en HTML DOM directamente. Si el código HTML inyectado tiene etiquetas SCRIPT dentro, no va a funcionar.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body></body><script>document.getElementsByTagName("body")[0].innerHTML = "<script>console.log('hi there')</script>\n<div>hello world</div>\n"</script></html>
Antonio Martín
fuente
-3

Otra cosa a hacer es cargar la página con un script como:

<div id="content" onmouseover='myFunction();$(this).prop( 'onmouseover', null );'>
<script type="text/javascript">
function myFunction() {
  //do something
}
myFunction();
</script>
</div>

Esto cargará la página, luego ejecutará el script y eliminará el controlador de eventos cuando se haya ejecutado la función. Esto no se ejecutará inmediatamente después de una carga ajax, pero si está esperando que el usuario ingrese el elemento div, esto funcionará bien.

PD. Requiere Jquery

Joshua Klein
fuente