Comment puis-je référencer la balise de script qui a chargé le script en cours d'exécution?

303

Comment puis-je référencer l'élément de script qui a chargé le javascript en cours d'exécution?

Voici la situation. J'ai un script "maître" chargé en haut de la page, la première chose sous la balise HEAD.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="scripts.js"></script>

Il y a un script dans "scripts.js" qui doit être capable de charger à la demande d'autres scripts. La méthode normale ne fonctionne pas tout à fait pour moi car j'ai besoin d'ajouter de nouveaux scripts sans référencer la balise HEAD, car l'élément HEAD n'a pas terminé le rendu:

document.getElementsByTagName('head')[0].appendChild(v);

Ce que je veux faire, c'est référencer l'élément de script qui a chargé le script actuel afin que je puisse ensuite ajouter mes nouvelles balises de script chargées dynamiquement dans le DOM après lui.

<script type="text/javascript" src="scripts.js"></script>
loaded by scripts.js--><script type="text/javascript" src="new_script1.js"></script>
loaded by scripts.js --><script type="text/javascript" src="new_script2.js"></script>
Shog9
la source
1
Un mot d'avertissement: la modification du DOM pendant qu'il est en cours de chargement vous causera un monde de blessures dans IE6 et IE7 . Vous ferez mieux d'exécuter ce code après le chargement de la page.
Triptyque
6
On dirait que c'est sur caniuse maintenant: caniuse.com/#feat=document-currentscript
Tyler

Réponses:

653

Comment obtenir l'élément de script actuel:

1. Utilisez document.currentScript

document.currentScriptretournera l' <script>élément dont le script est en cours de traitement.

<script>
var me = document.currentScript;
</script>

Avantages

  • Simple et explicite. Fiable.
  • Pas besoin de modifier la balise de script
  • Fonctionne avec des scripts asynchrones ( defer& async)
  • Fonctionne avec des scripts insérés dynamiquement

Problèmes

  • Ne fonctionnera pas dans les anciens navigateurs et IE.
  • Ne fonctionne pas avec les modules <script type="module">

2. Sélectionnez le script par id

Attribuer au script un attribut id vous permettra de le sélectionner facilement par id de l'intérieur en utilisant document.getElementById().

<script id="myscript">
var me = document.getElementById('myscript');
</script>

Avantages

  • Simple et explicite. Fiable.
  • Presque universellement pris en charge
  • Fonctionne avec des scripts asynchrones ( defer& async)
  • Fonctionne avec des scripts insérés dynamiquement

Problèmes

  • Nécessite l'ajout d'un attribut personnalisé à la balise de script
  • id l'attribut peut provoquer un comportement étrange pour les scripts dans certains navigateurs pour certains cas marginaux

3. Sélectionnez le script à l'aide d'un data-*attribut

Attribuer au script un data-*attribut vous permettra de le sélectionner facilement de l'intérieur.

<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>

Cela présente peu d'avantages par rapport à l'option précédente.

Avantages

  • Simple et explicite.
  • Fonctionne avec des scripts asynchrones ( defer& async)
  • Fonctionne avec des scripts insérés dynamiquement

Problèmes

  • Nécessite l'ajout d'un attribut personnalisé à la balise de script
  • HTML5, et querySelector()non compatible avec tous les navigateurs
  • Moins largement pris en charge que l'utilisation de l' idattribut
  • Va se déplacer <script>avec des idcas de bord.
  • Peut être confondu si un autre élément a le même attribut de données et la même valeur sur la page.

4. Sélectionnez le script par src

Au lieu d'utiliser les attributs de données, vous pouvez utiliser le sélecteur pour choisir le script par source:

<script src="//example.com/embed.js"></script>

Dans embed.js:

var me = document.querySelector('script[src="//example.com/embed.js"]');

Avantages

  • Fiable
  • Fonctionne avec des scripts asynchrones ( defer& async)
  • Fonctionne avec des scripts insérés dynamiquement
  • Aucun attribut ou identifiant personnalisé requis

Problèmes

  • Ne fonctionne pas pour les scripts locaux
  • Entraînera des problèmes dans différents environnements, comme le développement et la production
  • Statique et fragile. Changer l'emplacement du fichier de script nécessitera de modifier le script
  • Moins largement pris en charge que l'utilisation de l' idattribut
  • Cela causera des problèmes si vous chargez deux fois le même script

5. Parcourez tous les scripts pour trouver celui que vous voulez

Nous pouvons également parcourir tous les éléments de script et les vérifier individuellement pour sélectionner celui que nous voulons:

<script>
var me = null;
var scripts = document.getElementsByTagName("script")
for (var i = 0; i < scripts.length; ++i) {
    if( isMe(scripts[i])){
      me = scripts[i];
    }
}
</script>

Cela nous permet d'utiliser les deux techniques précédentes dans les anciens navigateurs qui ne prennent pas querySelector()bien en charge les attributs. Par exemple:

function isMe(scriptElem){
    return scriptElem.getAttribute('src') === "//example.com/embed.js";
}

Cela hérite des avantages et des problèmes de toute approche adoptée, mais ne dépend pas de querySelector()ce qui fonctionnera dans les navigateurs plus anciens.

6. Obtenez le dernier script exécuté

Comme les scripts sont exécutés séquentiellement, le dernier élément de script sera très souvent le script en cours d'exécution:

<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>

Avantages

  • Facile.
  • Presque universellement pris en charge
  • Aucun attribut ou identifiant personnalisé requis

Problèmes

  • Ne fonctionne pas avec les scripts asynchrones ( defer& async)
  • Ne fonctionne pas avec les scripts insérés dynamiquement
brice
la source
4
Cela devrait être la réponse.
Royi Namir
1
D'accord avec @RoyiNamir. C'est la meilleure réponse.
Hans
27
Merci les gars, mais vous savez que j'ai répondu 4 ans après la réponse acceptée, à droite :)
brice
5
"document.currentScript" ne fonctionne pas pour moi avec les scripts chargés dynamiquement, renvoie null dans le dernier chrome / firefox, "le dernier script exécuté" fonctionne bien
xwild
1
ne fonctionne pas lorsqu'il scriptest templateinséré dans un DOM fantôme
Supersharp
86

Étant donné que les scripts sont exécutés séquentiellement, la balise de script actuellement exécutée est toujours la dernière balise de script sur la page jusque-là. Donc, pour obtenir la balise de script, vous pouvez faire:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
Bouchée de café
la source
3
C'est simple et élégant. Vous en trouverez un exemple dans la nouvelle API Google Graphiques / Visualisations si vous décompressez le javascript. Ils chargent les données JSON à partir de la balise de script, voir: ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher
1
C'est une excellente idée, et cela fonctionne normalement pour moi. Mais je dois ajouter qu'il y a des moments où je l'ai trouvé renvoyant une référence à un script différent. Je ne sais pas pourquoi - je n'ai pas pu retrouver cela. Par conséquent, j'utilise généralement une méthode différente, par exemple, je code en dur le nom du fichier de script et recherche la balise de script avec ce nom de fichier.
Ken Smith,
53
Une instance à laquelle je peux penser où cela pourrait retourner des résultats incorrects est lorsqu'une balise de script est ajoutée au DOM de manière asynchrone.
Coffee Bite
9
Oui, cela peut avoir des résultats imprévisibles, vous pouvez donc essayer d'utiliser un sélecteur à la place: $ ('script [src * = "/ mysource.js"]') ???
Jason Sebring
7
Cela ne fonctionne pas lorsque vous avez des scripts chargés après le chargement de la page. Vous n'obtiendrez probablement pas la bonne balise.
ThemeZ
11

La chose la plus simple à faire serait probablement d'attribuer à votre balise de script un idattribut.

Greg
la source
2
Bien que vous ayez raison, il existe de nombreux cas dans lesquels la question du PO est valide, un couple serait: 1) lorsque vous explorez 2) lorsque vous travaillez avec le DOM d'un client, et qu'il n'est pas disposé à changer
nichochar
10

Les scripts ne sont exécutés séquentiellement que s'ils n'ont pas d'attribut "defer" ou "async". La connaissance de l'un des attributs ID / SRC / TITLE possibles de la balise de script pourrait également fonctionner dans ces cas. Les suggestions de Greg et Justin sont donc correctes.

Il y a déjà une proposition pour un document.currentScriptsur les listes WHATWG.

EDIT : Firefox> 4 implémente déjà cette propriété très utile mais elle n'est pas disponible dans IE11 la dernière fois que j'ai vérifié et uniquement disponible dans Chrome 29 et Safari 8.

EDIT : Personne n'a mentionné la collection "document.scripts" mais je pense que ce qui suit peut être une bonne alternative multi-navigateur pour obtenir le script en cours d'exécution:

var me = document.scripts[document.scripts.length -1];
Diego Perini
la source
1
C'est document.scripts pas document.script
Moritz
9

Voici un peu d'un polyfill qui tire parti document.CurrentScripts'il existe et revient à trouver le script par ID.

<script id="uniqueScriptId">
    (function () {
        var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');

        // your code referencing thisScript here
    ());
</script>

Si vous l'incluez en haut de chaque balise de script, je pense que vous pourrez toujours savoir quelle balise de script est déclenchée, et vous pourrez également référencer la balise de script dans le contexte d'un rappel asynchrone.

Non testé, alors laissez des commentaires aux autres si vous l'essayez.

Art Lawry
la source
L' idattribut n'est cependant pas valide dans un scriptélément. Quels types de problèmes cette approche pourrait-elle générer?
nr
1
@nr - Non, tous les éléments peuvent avoir un idattribut. id,, classet slotsont définis au niveau DOM, pas au niveau HTML. Si vous accédez aux attributs globaux en HTML et faites défiler la liste, vous constaterez que «la norme DOM définit les exigences de l'agent utilisateur pour les attributs de classe, id et slot pour tout élément dans n'importe quel espace de noms». suivi de "Les attributs class, id et slot peuvent être spécifiés sur tous les éléments HTML." La spécification DOM le couvre ici .
TJ Crowder
6

Il doit fonctionner au chargement de la page et lorsqu'une balise de script est ajoutée avec javascript (ex. Avec ajax)

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
David Callizaya
la source
3

Suivez ces étapes simples pour obtenir une référence au bloc de script en cours d'exécution:

  1. Mettez une chaîne unique aléatoire dans le bloc de script (doit être unique / différente dans chaque bloc de script)
  2. Itérer le résultat de document.getElementsByTagName ('script'), en regardant la chaîne unique de chacun de leur contenu (obtenu à partir de la propriété innerText / textContent).

Exemple (ABCDE345678 est l'ID unique) :

<script type="text/javascript">
var A=document.getElementsByTagName('script'),i=count(A),thi$;
for(;i;thi$=A[--i])
  if((thi$.innerText||thi$.textContent).indexOf('ABCDE345678'))break;
// Now thi$ is refer to current script block
</script>

btw, pour votre cas, vous pouvez simplement utiliser la méthode document.write () à l'ancienne pour inclure un autre script. Comme vous avez mentionné que le DOM n'est pas encore rendu, vous pouvez profiter du fait que le navigateur exécute toujours le script dans une séquence linéaire (à l'exception de celui différé qui sera rendu plus tard), de sorte que le reste de votre document est toujours "inexistant". Tout ce que vous écrivez via document.write () sera placé juste après le script de l'appelant.

Exemple de page HTML d'origine :

<!doctype html>
<html><head>
<script src="script.js"></script>
<script src="otherscript.js"></script>
<body>anything</body></html>

Contenu de script.js :

document.write('<script src="inserted.js"></script>');

Après rendu, la structure DOM deviendra:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY
Ferdinand Liu
la source
Cela semble fonctionner uniquement pour les scripts en ligne, pas pour les scripts externes. Dans ce dernier cas, toutes les propriétés innerText, text et textContent sont vides.
Jos de Jong
3

Une approche pour gérer les scripts asynchrones et différés consiste à utiliser le gestionnaire onload - définissez un gestionnaire onload pour toutes les balises de script et le premier qui s'exécute doit être le vôtre.

function getCurrentScript(callback) {
  if (document.currentScript) {
    callback(document.currentScript);
    return;
  }
  var scripts = document.scripts;
  function onLoad() {
    for (var i = 0; i < scripts.length; ++i) {
      scripts[i].removeEventListener('load', onLoad, false);
    }
    callback(event.target);
  }
  for (var i = 0; i < scripts.length; ++i) {
    scripts[i].addEventListener('load', onLoad, false);
  }
}

getCurrentScript(function(currentScript) {
  window.console.log(currentScript.src);
});
Pete Blois
la source
3

Pour obtenir le script, qui a actuellement chargé le script, vous pouvez utiliser

var thisScript = document.currentScript;

Vous devez conserver une référence au début de votre script, afin que vous puissiez appeler plus tard

var url = thisScript.src
Peter Lakatos - appnomads
la source
2

Considérez cet algorithme. Lorsque votre script se charge (s'il existe plusieurs scripts identiques), parcourez document.scripts, recherchez le premier script avec l'attribut "src" correct, enregistrez-le et marquez-le comme "visité" avec un attribut de données ou un nom de classe unique.

Lorsque le prochain script se charge, parcourez à nouveau le document.scripts, en passant sur tout script déjà marqué comme visité. Prenez la première instance non visitée de ce script.

Cela suppose que des scripts identiques s'exécuteront probablement dans l'ordre dans lequel ils sont chargés, de la tête au corps, de haut en bas, de synchrone à asynchrone.

(function () {
  var scripts = document.scripts;

  // Scan for this data-* attribute
  var dataAttr = 'data-your-attribute-here';

  var i = 0;
  var script;
  while (i < scripts.length) {
    script = scripts[i];
    if (/your_script_here\.js/i.test(script.src)
        && !script.hasAttribute(dataAttr)) {

        // A good match will break the loop before
        // script is set to null.
        break;
    }

    // If we exit the loop through a while condition failure,
    // a check for null will reveal there are no matches.
    script = null;
    ++i;
  }

  /**
   * This specific your_script_here.js script tag.
   * @type {Element|Node}
   */
  var yourScriptVariable = null;

  // Mark the script an pass it on.
  if (script) {
    script.setAttribute(dataAttr, '');
    yourScriptVariable = script;
  }
})();

Cela va parcourir tout le script pour le premier script correspondant qui n'est pas marqué avec l'attribut spécial.

Ensuite, marquez ce nœud, s'il est trouvé, avec un attribut de données afin que les analyses suivantes ne le choisissent pas. Ceci est similaire aux algorithmes BFS et DFS de traversée de graphe où les nœuds peuvent être marqués comme «visités» pour empêcher une nouvelle visite.

LSOJ
la source
Bienvenue dans Stack Overflow. Souhaitez-vous inclure du code avec l'algorithme?
Gary99
Thar you go, @ Gary99
LSOJ
0

J'ai ceci, qui fonctionne dans FF3, IE6 et 7. Les méthodes dans les scripts chargés à la demande ne sont pas disponibles jusqu'à ce que le chargement de la page soit terminé, mais cela est toujours très utile.

//handle on-demand loading of javascripts
makescript = function(url){
    var v = document.createElement('script');
    v.src=url;
    v.type='text/javascript';

    //insertAfter. Get last <script> tag in DOM
    d=document.getElementsByTagName('script')[(document.getElementsByTagName('script').length-1)];
    d.parentNode.insertBefore( v, d.nextSibling );
}

la source
0

Si vous pouvez assumer le nom de fichier du script, vous pouvez le trouver. Jusqu'à présent, je n'ai vraiment testé que la fonction suivante dans Firefox.

  function findMe(tag, attr, file) {
    var tags = document.getElementsByTagName(tag);
    var r = new RegExp(file + '$');
    for (var i = 0;i < tags.length;i++) {
      if (r.exec(tags[i][attr])) {
        return tags[i][attr];
      }
    }
  };
  var element = findMe('script', 'src', 'scripts.js');
Juste amoureux
la source
1
Très obsolète. Cela peut être fait avec le querySelector, un simple oneliner!
Fabian von Ellerts
-1

J'ai trouvé que le code suivant était le plus cohérent, le plus performant et le plus simple.

var scripts = document.getElementsByTagName('script');
var thisScript = null;
var i = scripts.length;
while (i--) {
  if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) {
    thisScript = scripts[i];
    break;
  }
}
console.log(thisScript);
Travis Tidwell
la source