Exécution de <script> injecté par innerHTML après un appel AJAX

100

Il existe un div appelé "Contenu":

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

Il doit être rempli avec les données d'un fichier PHP, par AJAX, y compris une <script>balise. Cependant, le script à l'intérieur de cette balise n'est pas en cours d'exécution.

<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
la source
Comment chargez-vous le div? dépend de la bibliothèque que vous utilisez, vous pouvez normalement contrôler si vous voulez simplement exécuter les scripts après le chargement ajax.
Bill Yang le
1
Après window.onloadavoir créé un XMTHttpRequestobjet pour demander une autre page (php) contenant le contenu du div, y compris un script. Je fais cela avec JS ordinaire, pas de bibliothèques (autre que la mienne lol)
JCOC611

Réponses:

66

Le JavaScript inséré en tant que texte DOM ne s'exécutera pas. Cependant, vous pouvez utiliser le modèle de script dynamique pour atteindre votre objectif. L'idée de base est de déplacer le script que vous souhaitez exécuter dans un fichier externe et de créer une balise de script lorsque vous obtenez votre réponse Ajax. Vous définissez ensuite l' srcattribut de votre balise de script et voilà, il charge et exécute le script externe.

Cet autre article de StackOverflow peut également vous être utile: les scripts peuvent-ils être insérés avec innerHTML? .

Chocula
la source
vous pouvez sélectionner tous les scripts chargés et les exécuter par la fonction eval ():$('#audit-view script').each(function (index, element) { eval(element.innerHTML); })
Jerzy Gebler
Javascript ajouté à la page doit absolument s'exécuter. ( jsfiddle.net/wnn5fz3m/1 par exemple). La question est pourquoi pas parfois?
NoBugs
@Chocula J'ai déplacé mon script dans un fichier séparé, mais cela ne fonctionne toujours pas à partir de partiel, pouvez-vous m'aider? stackoverflow.com/questions/42941856/…
Samra
FYI (@NoBugs me donne un indice). J'ai essayé plain javascirpt element.innerHTML = zzz et desnt fonctionnent. Ensuite, j'essaye Jquery $ (element) .html (zzz) et j'ai travaillé.
gtryonp
66

J'ai utilisé ce code, il fonctionne bien

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div
Firas Nizam
la source
4
Si vous me demandez, c'est une bien meilleure réponse que celle acceptée. C'est à peu près une injection JavaScript.
Xedret
Bien qu'utile, cela ne répond pas vraiment à la question de savoir pourquoi les scripts ne s'exécuteraient pas . Voir par exemple jsfiddle.net/fkqmcaz7 et jsfiddle.net/wnn5fz3m/1 Il devrait absolument fonctionner, et je ne trouve pas de cas simplifié où cela ne fonctionne pas.
NoBugs
1
@NaoiseGolden OP pose littéralement des questions sur l'exécution du script, donc je pense que ça va ici. :-)
tourne
1
Eval () n'est pas fortement recommandé. Voir ça - stackoverflow.com/questions/9107847/…
Kings
15

Si vous chargez un bloc de script dans votre div via Ajax comme ceci:

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

... il met simplement à jour le DOM de votre page, myFunction () n'est pas forcément appelé.

Vous pouvez utiliser une méthode de rappel Ajax telle que celle de jQuery méthode ajax () pour définir ce qu'il faut exécuter lorsque la requête se termine.

Ce que vous faites est différent du chargement d'une page avec JavaScript inclus dès le départ (qui est exécuté).

Un exemple d'utilisation du rappel de succès et du rappel d'erreur après avoir récupéré du contenu:

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

Un autre moyen rapide et sale est d'utiliser eval () pour exécuter tout code de script que vous avez inséré en tant que texte DOM si vous ne souhaitez pas utiliser jQuery ou une autre bibliothèque.

Christopher Tokar
la source
en utilisant le rappel de jQuery, comment pourrais-je "exécuter" le code à l'intérieur du nouveau <script>?
JCOC611
J'ai ajouté un exemple d'utilisation du rappel de succès:
Christopher Tokar
1
Merci beaucoup! le "myFunction ();" une partie était ce que je laissais hors de mon code.
Jason
11

Voici le script qui évaluera toutes les balises de script dans le texte.

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

Appelez simplement cette fonction après avoir reçu votre code HTML du serveur. Soyez averti: utilisationeval peut être dangereuse.

Démo: http://plnkr.co/edit/LA7OPkRfAtgOhwcAnLrl?p=preview

Evgenii
la source
5

Cela `` fonctionne '' pour moi en utilisant jQuery, à condition que vous n'essayiez pas d'ajouter un sous-ensemble du HTML renvoyé par XHR au document. (Voir ce rapport de bogue montrant le problème avec jQuery.)

Voici un exemple montrant que cela fonctionne:

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

Voici l'équivalent de ce qui précède: http://jsfiddle.net/2CTLH/

Phrogz
la source
1
Très peu probable, ce serait la solution au problème actuellement, car il s'agissait d'un bogue avec une version vraiment ancienne de jQuery.
NoBugs
3

Voici une fonction que vous pouvez utiliser pour analyser les réponses AJAX, en particulier si vous utilisez minifiedjs et que vous voulez qu'il exécute le Javascript retourné ou que vous voulez simplement analyser les scripts sans les ajouter au DOM, il gère également les erreurs d'exception. J'ai utilisé ce code dans la bibliothèque php4sack et il est utile en dehors de la bibliothèque.

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;
 }
André Van Zuydam
la source
2

Si vous injectez quelque chose qui nécessite la balise de script, vous pouvez obtenir une erreur de syntaxe non interceptée et dire un jeton illégal . Pour éviter cela, veillez à éviter les barres obliques dans votre ou vos balises de script de fermeture. c'est à dire;

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

Il en va de même pour toutes les balises de fermeture, telles qu'une balise de formulaire.

TommyRay
la source
0

J'ai eu un message similaire ici, charge addEventListener sur la charge ajax SANS jquery

La façon dont je l'ai résolu a consisté à insérer des appels à des fonctions dans ma fonction stateChange. La page que j'avais configurée était 3 boutons qui chargeraient 3 pages différentes dans contentArea. Parce que je devais savoir quel bouton était enfoncé pour charger la page 1, 2 ou 3, je pourrais facilement utiliser des instructions if / else pour déterminer quelle page est chargée et ensuite quelle fonction exécuter. Ce que j'essayais de faire était d'enregistrer différents écouteurs de boutons qui ne fonctionneraient que lorsque la page spécifique était chargée à cause des ID d'élément.

alors...

si (page1 est en cours de chargement, pageload = 1) exécuter la fonction registerListeners1

puis la même chose pour la page 2 ou 3.

Richard Chase
la source
0

Cela a fonctionné pour moi en appelant eval sur chaque contenu de script depuis ajax .done:

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

Remarque: je n'ai pas écrit de paramètres dans $ .ajax que vous devez ajuster en fonction de votre ajax.

shivgre
la source
0

Ma conclusion est que HTML n'autorise pas les balises NESTED SCRIPT. Si vous utilisez javascript pour injecter du code HTML contenant des balises de script, cela ne fonctionnera pas car le javascript entre également dans une balise de script. Vous pouvez le tester avec le code suivant et vous serez convaincu que cela ne fonctionnera pas. Le cas d'utilisation est que vous appelez un service avec AJAX ou similaire, vous obtenez du HTML et vous souhaitez l'injecter directement dans le DOM HTML. Si le code HTML injecté contient des balises SCRIPT, cela ne fonctionnera pas.

<!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 Martin
la source
-3

Une autre chose à faire est de charger la page avec un script tel que:

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

Cela chargera la page, puis exécutera le script et supprimera le gestionnaire d'événements une fois la fonction exécutée. Cela ne fonctionnera pas immédiatement après un chargement ajax, mais si vous attendez que l'utilisateur entre l'élément div, cela fonctionnera très bien.

PS. Nécessite Jquery

Joshua Klein
la source