Impossible d'ajouter l'élément <script>

276

Une idée pourquoi le morceau de code ci-dessous n'ajoute pas l'élément de script au DOM?

var code = "<script></script>";
$("#someElement").append(code);
Dan Burzo
la source
4
définir "ne fonctionne pas" - bien que je soupçonne que le problème est que les scripts ne font pas vraiment partie de l'arborescence dom.
Joel Coehoorn
1
Je parie que le nœud de script est ajouté au DOM, mais le navigateur n'exécute tout simplement pas le script.
Programmeur hors
Dan, comment avez-vous réellement testé cela?
James
1
@Joel - "not working" = il n'a aucun effet, c'est-à-dire que le code dans le script n'est pas exécuté @Outlaw Programmer - le noeud n'est pas ajouté au DOM @Jimmy Oui, je l'ai testé et ça ne fonctionne pas
Dan Burzo

Réponses:

259

J'ai vu des problèmes où certains navigateurs ne respectent pas certaines modifications lorsque vous les faites directement (j'entends par là la création du HTML à partir de texte comme vous essayez avec la balise script), mais lorsque vous les faites avec des commandes intégrées les choses vont mieux. Essaye ça:

var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
$("#someElement").append( script );

De: JSON pour jQuery

acrosman
la source
18
La balise <script> doit être ajoutée au DOM lui-même, et non au innerHTML d'un élément existant.
Diodeus - James MacFarlane
6
@Diodeus le innerHTML fait partie du DOM et il est analysé à la volée par le navigateur, c'est donc un cas valide.
Cristian Toma
5
Ne vous contentez pas d'utiliser l'append de jQuery si vous voulez vraiment que l'élément apparaisse dans le DOM. Utilisez l'appendChild natif pour y parvenir.
Minqi Pan
8
$("#someElement")[0].appendChild( script );
Minqi Pan
352

La bonne nouvelle c'est:

Il fonctionne à 100%.

Ajoutez simplement quelque chose à l'intérieur de la balise de script tel que alert('voila!');. La bonne question que vous voudrez peut-être vous poser, "Pourquoi ne l'ai-je pas vue dans le DOM?" .

Karl Swedberg a fait une belle explication au commentaire du visiteur dans le site de l'API jQuery . Je ne veux répéter ses mots, vous pouvez lire directement là - bas ici (je l' ai trouvé difficile de naviguer à travers les commentaires là - bas) .

Toutes les méthodes d'insertion de jQuery utilisent une fonction domManip en interne pour nettoyer / traiter les éléments avant et après leur insertion dans le DOM. L'une des fonctions de la fonction domManip est d'extraire tous les éléments de script sur le point d'être insérés et de les exécuter via une "routine evalScript" plutôt que de les injecter avec le reste du fragment DOM. Il insère les scripts séparément, les évalue, puis les supprime du DOM.

Je pense que l'une des raisons pour lesquelles jQuery le fait est d'éviter les erreurs "Autorisation refusée" qui peuvent se produire dans Internet Explorer lors de l'insertion de scripts dans certaines circonstances. Cela évite également d'insérer / d'évaluer de manière répétée le même script (ce qui pourrait potentiellement causer des problèmes) s'il se trouve dans un élément conteneur que vous insérez puis déplacez-vous dans le DOM.

La prochaine chose est, je vais résumer quelles sont les mauvaises nouvelles en utilisant la .append()fonction pour ajouter un script.


Et la mauvaise nouvelle est ..

Vous ne pouvez pas déboguer votre code.

Je ne plaisante pas, même si vous ajoutez un debugger;mot-clé entre la ligne que vous souhaitez définir comme point d'arrêt, vous finirez par n'obtenir que la pile d'appels de l'objet sans voir le point d'arrêt sur le code source, (sans oublier que cela mot-clé ne fonctionne que dans le navigateur webkit, tous les autres principaux navigateurs semblent omettre ce mot-clé) .

Si vous comprenez parfaitement ce que fait votre code, ce sera un inconvénient mineur. Mais si vous ne le faites pas, vous finirez par ajouter un debugger;mot - clé partout pour découvrir ce qui ne va pas avec votre (ou mon) code. Quoi qu'il en soit, il existe une alternative, n'oubliez pas que javascript peut manipuler nativement HTML DOM.


Solution de contournement.

Utilisez javascript (pas jQuery) pour manipuler HTML DOM

Si vous ne voulez pas perdre la capacité de débogage, vous pouvez utiliser la manipulation DOM HTML natif javascript. Considérez cet exemple:

var script   = document.createElement("script");
script.type  = "text/javascript";
script.src   = "path/to/your/javascript.js";    // use this for linked script
script.text  = "alert('voila!');"               // use this for inline script
document.body.appendChild(script);

Ça y est, tout comme le bon vieux temps n'est-ce pas. Et n'oubliez pas de nettoyer les choses que ce soit dans le DOM ou dans la mémoire pour tous les objets référencés et qui ne sont plus nécessaires pour éviter les fuites de mémoire. Vous pouvez considérer ce code pour nettoyer les choses:

document.body.removechild(document.body.lastChild);
delete UnusedReferencedObjects; // replace UnusedReferencedObject with any object you created in the script you load.

L'inconvénient de cette solution de contournement est que vous pouvez accidentellement ajouter un script en double, et c'est mauvais. De là, vous pouvez légèrement imiter la .append()fonction en ajoutant une vérification d'objet avant d'ajouter et en supprimant le script du DOM juste après son ajout. Considérez cet exemple:

function AddScript(url, object){
    if (object != null){
        // add script
        var script   = document.createElement("script");
        script.type  = "text/javascript";
        script.src   = "path/to/your/javascript.js";
        document.body.appendChild(script);

        // remove from the dom
        document.body.removeChild(document.body.lastChild);
        return true;
    } else {
        return false;
    };
};

function DeleteObject(UnusedReferencedObjects) {
    delete UnusedReferencedObjects;
}

De cette façon, vous pouvez ajouter un script avec une capacité de débogage tout en étant à l'abri de la duplicité du script. Ce n'est qu'un prototype, vous pouvez développer ce que vous voulez. J'utilise cette approche et j'en suis assez satisfait. Effectivement, je n'utiliserai jamais jQuery .append()pour ajouter un script.

Hendra Uzia
la source
2
Excellente explication, merci! Mais était "UnusedReferencedObjects"?
Acme
Merci. Je suppose que vous faisiez référence à la première occurrence de "UnusedReferencedObjects", qui était censé être tout objet que vous avez créé dans le script que vous chargez. Je suppose que vous comprenez mieux si vous voyez ce que fait la fonction DeleteObject, cela supprime simplement tout objet que vous avez passé à la fonction. Je vais ajouter quelques commentaires dans le code pour être clair. :)
Hendra Uzia
Super post! L'exemple d'aCrosman n'a pas fonctionné pour moi et après avoir préparé votre message, j'ai remplacé $ ("# someElement"). Append (script); avec $ ("# someElement") [0] .appendChild (script); Ce qui l'a corrigé :) Thnx pour l'explantion
Maarten Kieft
7
$.getScriptest intégré à jQuery.
zzzzBov
Les URL source ( blog.getfirebug.com/2009/08/11/… ) peuvent aider à résoudre les problèmes de débogage.
vsevik
23

Il est possible de charger dynamiquement un fichier JavaScript à l'aide de la fonction jQuerygetScript

$ .getScript ('http://www.wwhat.com/shareprice/shareprice.js', function () {
  Display.sharePrice ();
});

Maintenant, le script externe sera appelé et s'il ne peut pas être chargé, il se dégradera avec élégance.

Cueball
la source
7
Cela n'insère rien dans le DOM. Ça appelle eval().
nh2
20

Que voulez-vous dire "ne fonctionne pas"?

jQuery détecte que vous essayez de créer un élément SCRIPT et exécutera automatiquement le contenu de l'élément dans le contexte global. Êtes-vous en train de me dire que cela ne fonctionne pas pour vous? -

$('#someElement').append('<script>alert("WORKING");</script>');

Edit: Si vous ne voyez pas l'élément SCRIPT dans le DOM (dans Firebug par exemple) après avoir exécuté la commande, c'est parce que jQuery, comme je l'ai dit, exécutera le code puis supprimera l'élément SCRIPT - je crois que les éléments SCRIPT sont toujours ajoutés au corps ... mais de toute façon - le placement n'a absolument aucune incidence sur l'exécution du code dans cette situation.

James
la source
17

Cela marche:

$('body').append($("<script>alert('Hi!');<\/script>")[0]);

Il semble que jQuery fasse quelque chose d'intelligent avec les scripts, vous devez donc ajouter l'élément html plutôt que l'objet jQuery.

Darwin
la source
2
C'est la seule solution qui fonctionne dans mon cas. Les autres solutions ci-dessus dépendent du code javascript présent à l'avance dans le fichier html. Comme j'ajoute du code dynamiquement, un tel code javascript ne peut pas être connu à l'avance! MERCI Darwin !!
tgoneil
14

Essayez ceci peut être utile:

var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src","scriptAnalytics.js");
document.getElementsByTagName("head")[0].appendChild(fileref);
Jainul Khan
la source
9
aucune idée pourquoi ce gars négatif est le seul pas assez stupide pour continuer à utiliser jquery pour tout!
SS a parlé
La plupart des réponses utilisent probablement jQuery car dans la question, l'OP utilise jQuery.
sanbor
3

Je veux faire la même chose mais ajouter une balise de script dans un autre cadre!

var url = 'library.js'; 
var script = window.parent.frames[1].document.createElement('script' ); 
script.type = 'text/javascript'; 
script.src = url;
$('head',window.parent.frames[1].document).append(script);
Julio
la source
3
<script>
    ...
    ...jQuery("<script></script>")...
    ...
</script>

Le </script>littéral dans la chaîne termine le script entier, pour éviter qu'il "</scr" + "ipt>"puisse être utilisé à la place.

Roman Odaisky
la source
Ou n'intégrez pas votre javascript directement dans le document HTML. Les fichiers de script externes n'ont pas ce problème.
Ian Clelland
Séquences d' échappement: "\x3cscript\x3e\x3c/script\x3e". En XHTML, il vous suffit de mettre un bloc CDATA commenté.
Eli Gray
2
C'est plutôt ce </qui n'est pas autorisé. Voir stackoverflow.com/questions/236073/…
Gumbo
2

L'ajout de l'URL source dans le fichier de script a aidé comme mentionné dans cette page: https://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/

  1. Dans le fichier de script, ajoutez une instruction avec sourceURL comme "// @ sourceURL = foo.js"
  2. Chargez le script à l'aide de jQuery $ .getScript () et le script sera disponible dans l'onglet "sources" des outils de développement de chrome
Naga Kiran
la source
1

Votre script est en cours d'exécution, vous ne pouvez tout simplement pas l'utiliser document.write. Utilisez une alerte pour le tester et éviter de l'utiliser document.write. Les instructions de votre fichier js document.writene seront pas exécutées et le reste de la fonction sera exécuté.

Kunal Kinalekar
la source
1

C'est ce que je pense être la meilleure solution. Google Analytics est injecté de cette façon.

var (function(){
    var p="https:" == document.location.protocol ? "https://" : "http://";
        d=document,
        g=d.createElement('script'),
        s=d.getElementsByTagName('script')[0];
        g.type='text/javascript';
        g.src=p+'url-to-your-script.js';
        s.parentNode.insertBefore(g,s); })();
dzona
la source
1

Vous n'avez pas besoin de jQuery pour créer un élément DOM de script . Cela peut être fait avec vanilla ES6 comme ceci:

const script = "console.log('Did it work?')"
new Promise((resolve, reject) => {
  (function(i,s,o,g,r,a,m){
      a=s.createElement(o),m=s.getElementsByTagName(o)[0];
      a.innerText=g;
      a.onload=r;m.parentNode.insertBefore(a,m)}
  )(window,document,'script',script, resolve())
}).then(() => console.log('Sure did!'))

Il n'a pas besoin d'être enveloppé dans un Promise, mais cela vous permet de résoudre la promesse lorsque le script se charge, ce qui permet d'éviter les conditions de concurrence pour les scripts de longue durée.

Josh Habdas
la source
0

Ajoutez le script au corps:

$(document).ready(function() {
    $("<script>", {  src : "bootstrap.min.js",  type : "text/javascript" }).appendTo("body");
});
Cristian Olaru
la source
0

Vous pouvez également le faire si vous souhaitez ajouter du code en utilisant la document.createElementméthode, mais en utilisant à la .innerHTMLplace de .src.

var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.innerHTML = 'alert("Hey there... you just appended this script to the body");';
$("body").append( script );
Marc
la source
0

J'ai essayé celui- ci et fonctionne très bien. Remplacez simplement le <symbole par cela \x3C.

// With Variable
var code = "\x3Cscript>SomeCode\x3C/script>";
$("#someElement").append(code);

ou

//Without Variable
$("#someElement").append("\x3Cscript>SomeCode\x3C/script>");

Vous pouvez tester le code ici .

Andrew
la source
0

Peut essayer comme ça

var code = "<script></" + "script>";
$("#someElement").append(code);

La seule raison pour laquelle vous ne pouvez pas le faire "<script></script>"est que la chaîne n'est pas autorisée dans javascript car la couche DOM ne peut pas analyser ce qui est js et ce qui est HTML.

VipinKundal
la source
0

J'ai écrit un npmpackage qui vous permet de prendre une chaîne HTML, y compris des balises de script et de l'ajouter à un conteneur lors de l' exécution des scripts

Exemple:

import appendHtml from 'appendhtml';

const html = '<p>Hello</p><script src="some_js_file.js"></script>'; 
const container = document.getElementById('some-div');

await appendHtml(html, container);

// appendHtml returns a Promise, some_js_file.js is now loaded and executed (note the await)

Trouvez-le ici: https://www.npmjs.com/package/appendhtml

Lukas
la source
-1

Créez simplement un élément en l'analysant avec jQuery.

<div id="someElement"></div>
<script>
    var code = "<script>alert(123);<\/script>";
    $("#someElement").append($(code));
</script>

Exemple de travail: https://plnkr.co/edit/V2FE28Q2eBrJoJ6PUEBz

sanbor
la source
Cela a toujours le même problème, certains navigateurs ne le respectent pas.
Martin Massera
Pourriez-vous me faire savoir quels navigateurs et quelle version de jQuery utilisez-vous? Merci!
sanbor