Comment 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é haut dans la page, d'abord 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>
"Scripts.js" contient un script qui doit pouvoir charger d'autres scripts à la demande. La méthode normale ne fonctionne pas très bien pour moi car je dois ajouter de nouveaux scripts sans faire référence à 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 faire référence à l'élément de script qui a chargé le script actuel pour pouvoir ensuite ajouter mes nouvelles balises de script chargées dynamiquement dans le DOM après celui-ci.
<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>
document.currentScript
document.currentScript
renverra l'élément <script>
dont le script est en cours de traitement.
<script>
var me = document.currentScript;
</script>
defer
& async
)En attribuant au script un attribut id, vous pourrez facilement le sélectionner par identifiant depuis document.getElementById()
<script id="myscript">
var me = document.getElementById('myscript');
</script>
defer
& async
)id
peut provoquer un comportement étrange des scripts dans certains navigateurs pour certains cas Edgedata-*
Donner au script un attribut data-*
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.
defer
& async
)querySelector()
non conforme dans tous les navigateursid
<script>
avec id
cas Edge. 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"]');
defer
& async
)id
Nous pouvons également passer en boucle sur chaque élément de script et 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 navigateurs plus anciens qui ne supportent pas bien querySelector()
avec des attributs. Par exemple:
function isMe(scriptElem){
return scriptElem.getAttribute('src') === "//example.com/embed.js";
}
Cela hérite des avantages et des problèmes de l’approche choisie, mais ne repose pas sur querySelector()
et fonctionnera donc avec les anciens navigateurs.
Comme les scripts sont exécutés de manière séquentielle, 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>
defer
& async
)Étant donné que les scripts sont exécutés de manière séquentielle, la balise de script actuellement exécutée est toujours la dernière balise de script de la page. Donc, pour obtenir la balise script, vous pouvez faire:
var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
La chose la plus simple à faire serait probablement de donner à votre balise de script un attribut id
.
Les scripts ne sont exécutés de manière séquentielle que s'ils ne possèdent pas d'attribut "différer" ou "asynchrone". Connaître 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 existe déjà une proposition pour un document.currentScript
sur les listes du WHATWG.
EDIT: Firefox> 4 implémente déjà cette propriété très utile, mais elle n’est pas disponible dans IE11. J'ai vérifié et uniquement disponible dans Chrome 29 et Safari 8.
EDIT: Personne n’a mentionné la collection "document.scripts", mais j’estime que ce qui suit pourrait être une bonne alternative pour plusieurs navigateurs pour obtenir le script en cours d’exécution:
var me = document.scripts[document.scripts.length -1];
Voici un peu d'un polyfill qui exploite document.CurrentScript
s'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 incluez cela en haut de chaque balise de script, vous serez sûrement en mesure de savoir systématiquement 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 pour les autres si vous l'essayez.
Il doit fonctionner au chargement de la page et quand une balise de script est ajoutée avec javascript (ex. Avec ajax)
<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
Une approche permettant de gérer les scripts asynchrones et différés consiste à utiliser le gestionnaire de chargement onload pour définir un gestionnaire de chargement pour toutes les balises de script.
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);
});
Suivez ces étapes simples pour obtenir une référence au bloc de script en cours d'exécution:
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>
au fait, dans votre cas, vous pouvez simplement utiliser la méthode document.write () à l'ancienne pour inclure un autre script . Comme vous avez mentionné que DOM n'est pas encore restitué, vous pouvez tirer parti du fait que le navigateur exécute toujours le script de manière linéaire sequence (sauf celle différée qui sera rendue plus tard), donc le reste de votre document est toujours "non existant" . Tout ce que vous écrivez via document.write () sera placé juste après le script de l'appelant.
Exemple de page HTML originale :
<!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 du DOM deviendra:
HEAD
SCRIPT script.js
SCRIPT inserted.js
SCRIPT otherscript.js
BODY
Pour obtenir le script, celui qui le charge actuellement, vous pouvez utiliser
var thisScript = document.currentScript;
Vous devez conserver une référence au début de votre script pour pouvoir appeler plus tard.
var url = thisScript.src
Considérons cet algorithme. Lors du chargement de votre script (s'il existe plusieurs scripts identiques), parcourez document.scripts, recherchez le premier script avec le bon attribut "src", enregistrez-le et marquez-le comme "visité" avec un attribut de données ou un nom de classe unique.
Lors du chargement du script suivant, parcourez à nouveau document.scripts, en transmettant tout script déjà marqué comme visité. Prenez la première instance non consultée de ce script.
Cela suppose que des scripts identiques s'exécutent probablement dans l'ordre dans lequel ils sont chargés, de la tête au corps, de haut en bas, de synchrones à asynchrones.
(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 noeud, s'il est trouvé, avec un attribut de données afin que les analyses ultérieures ne le choisissent pas. Ceci est similaire aux algorithmes BFS et DFS de traversée de graphes, dans lesquels les nœuds peuvent être marqués comme étant "visités" pour empêcher toute nouvelle visite.
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);
Si vous pouvez assumer le nom de fichier du script, vous pouvez le trouver. Jusqu'à présent, j'ai seulement vraiment testé 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');
Il existe un autre moyen simple, si vous connaissez le nom du script (ou au moins une partie de celui-ci):
document.querySelector('script[src*="<scriptname>.js"]')
Cela fonctionnera également dans IE11.