Wie kann ich auf das Skript-Tag verweisen, das das aktuell ausgeführte Skript geladen hat?

303

Wie kann ich auf das Skriptelement verweisen, das das aktuell ausgeführte Javascript geladen hat?

Hier ist die Situation. Ich habe ein "Master" -Skript, das hoch oben auf der Seite geladen wird, als erstes unter dem HEAD-Tag.

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

In "scripts.js" befindet sich ein Skript, das in der Lage sein muss, andere Skripte bei Bedarf zu laden. Die normale Methode funktioniert bei mir nicht ganz, da ich neue Skripte hinzufügen muss, ohne auf das HEAD-Tag zu verweisen, da das HEAD-Element das Rendern noch nicht abgeschlossen hat:

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

Ich möchte auf das Skriptelement verweisen, das das aktuelle Skript geladen hat, damit ich meine neuen dynamisch geladenen Skript-Tags danach an das DOM anhängen kann.

<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
quelle
1
Ein Wort der Warnung: Wenn Sie das DOM ändern, während es noch geladen wird, werden Sie in IE6 und IE7 eine Welt voller Verletzungen haben . Sie sollten diesen Code nach dem Laden der Seite besser ausführen.
Triptychon
6
Sieht so aus, als ob es jetzt auf caniuse geht: caniuse.com/#feat=document-currentscript
Tyler

Antworten:

653

So erhalten Sie das aktuelle Skriptelement:

1. Verwenden Sie document.currentScript

document.currentScriptgibt das <script>Element zurück, dessen Skript gerade verarbeitet wird.

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

Leistungen

  • Einfach und explizit. Zuverlässig.
  • Das Skript-Tag muss nicht geändert werden
  • Funktioniert mit asynchronen Skripten ( defer& async)
  • Funktioniert mit dynamisch eingefügten Skripten

Probleme

  • Funktioniert nicht in älteren Browsern und im Internet Explorer.
  • Funktioniert nicht mit Modulen <script type="module">

2. Wählen Sie das Skript anhand der ID aus

Wenn Sie dem Skript ein ID-Attribut geben, können Sie es einfach anhand der ID von innen auswählen document.getElementById().

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

Leistungen

  • Einfach und explizit. Zuverlässig.
  • Fast universell unterstützt
  • Funktioniert mit asynchronen Skripten ( defer& async)
  • Funktioniert mit dynamisch eingefügten Skripten

Probleme

  • Erfordert das Hinzufügen eines benutzerdefinierten Attributs zum Skript-Tag
  • id Das Attribut kann in einigen Browsern in einigen Randfällen zu einem merkwürdigen Verhalten von Skripten führen

3. Wählen Sie das Skript mit einem data-*Attribut aus

Wenn Sie dem Skript ein data-*Attribut geben, können Sie es einfach von innen auswählen.

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

Dies hat gegenüber der vorherigen Option nur wenige Vorteile.

Leistungen

  • Einfach und explizit.
  • Funktioniert mit asynchronen Skripten ( defer& async)
  • Funktioniert mit dynamisch eingefügten Skripten

Probleme

  • Erfordert das Hinzufügen eines benutzerdefinierten Attributs zum Skript-Tag
  • HTML5 und querySelector()nicht in allen Browsern kompatibel
  • Weniger weit verbreitet als die Verwendung des idAttributs
  • Bekomme um <script>mit idRand Fällen.
  • Kann verwirrt werden, wenn ein anderes Element dasselbe Datenattribut und denselben Wert auf der Seite hat.

4. Wählen Sie das Skript von src aus

Anstatt die Datenattribute zu verwenden, können Sie den Skript mithilfe des Selektors nach Quelle auswählen:

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

In embedded.js:

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

Leistungen

  • Zuverlässig
  • Funktioniert mit asynchronen Skripten ( defer& async)
  • Funktioniert mit dynamisch eingefügten Skripten
  • Keine benutzerdefinierten Attribute oder ID erforderlich

Probleme

  • Funktioniert nicht für lokale Skripte
  • Verursacht Probleme in verschiedenen Umgebungen wie Entwicklung und Produktion
  • Statisch und zerbrechlich. Um den Speicherort der Skriptdatei zu ändern, muss das Skript geändert werden
  • Weniger weit verbreitet als die Verwendung des idAttributs
  • Verursacht Probleme, wenn Sie dasselbe Skript zweimal laden

5. Durchlaufen Sie alle Skripte, um das gewünschte zu finden

Wir können auch jedes Skriptelement durchlaufen und jedes einzeln überprüfen, um das gewünschte auszuwählen:

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

Auf diese Weise können wir beide vorherigen Techniken in älteren Browsern verwenden, die querySelector()Attribute nicht gut unterstützen . Zum Beispiel:

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

Dies erbt die Vorteile und Probleme eines jeden Ansatzes, ist jedoch nicht darauf angewiesen, querySelector()dass dies in älteren Browsern funktioniert.

6. Holen Sie sich das zuletzt ausgeführte Skript

Da die Skripte nacheinander ausgeführt werden, ist das letzte Skriptelement sehr oft das aktuell ausgeführte Skript:

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

Leistungen

  • Einfach.
  • Fast universell unterstützt
  • Keine benutzerdefinierten Attribute oder ID erforderlich

Probleme

  • Funktioniert nicht mit asynchronen Skripten ( defer& async)
  • Funktioniert nicht mit dynamisch eingefügten Skripten
brice
quelle
4
Dies sollte die Antwort sein.
Royi Namir
1
Stimmen Sie mit @RoyiNamir überein. Dies ist die beste Antwort.
Hans
27
Danke Jungs, aber ihr wisst, dass ich 4 Jahre nach der akzeptierten Antwort geantwortet habe , richtig :)
brice
5
"document.currentScript" funktioniert bei mir nicht mit dynamisch geladenen Skripten, gibt im neuesten Chrome / Firefox null zurück, "zuletzt ausgeführtes Skript" funktioniert
einwandfrei
1
funktioniert nicht, wenn scriptin einem templatein einem Shadow DOM eingefügten
Supersharp
86

Da Skripte nacheinander ausgeführt werden, ist das aktuell ausgeführte Skript-Tag bis dahin immer das letzte Skript-Tag auf der Seite. Um das Skript-Tag zu erhalten, haben Sie folgende Möglichkeiten:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
Kaffeebiss
quelle
3
Das ist einfach und elegant. Es gibt ein Beispiel dafür in der neuen Google Charts / Visualizations-API, wenn Sie das Javascript entpacken. Sie laden JSON-Daten aus dem Skript-Tag, siehe: ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher
1
Das ist eine großartige Idee und funktioniert normalerweise bei mir. Aber ich sollte hinzufügen, dass es Zeiten gibt, in denen ich festgestellt habe, dass ein Verweis auf ein anderes Skript zurückgegeben wird. Ich weiß nicht warum - konnte das nicht aufspüren. Folglich gehe ich normalerweise mit einer anderen Methode vor, z. B. indem ich den Namen der Skriptdatei fest codiere und nach dem Skript-Tag mit diesem Dateinamen suche.
Ken Smith
53
Eine Instanz, an die ich denken kann, wo dies zu falschen Ergebnissen führen könnte, ist das asynchrone Hinzufügen eines Skript-Tags zum DOM.
Kaffee Bite
9
Ja, dies kann zu unvorhersehbaren Ergebnissen führen, sodass Sie stattdessen versuchen können, einen Selektor zu verwenden: $ ('script [src * = "/ mysource.js"]') ???
Jason Sebring
7
Es funktioniert nicht, wenn Sie Skripte nach dem Laden der Seite geladen haben. Sie werden wahrscheinlich nicht das richtige Tag bekommen.
ThemeZ
11

Am einfachsten wäre es wahrscheinlich, Ihrem Scrip-Tag ein idAttribut zu geben .

Greg
quelle
2
Obwohl Sie Recht haben, gibt es viele Fälle, in denen die Frage des OP gültig ist. Ein Paar wäre: 1) wenn Sie crawlen 2) wenn Sie mit dem DOM eines Kunden arbeiten und er nicht bereit ist, Änderungen vorzunehmen
Nichochar
10

Skripte werden nur nacheinander ausgeführt, wenn sie weder ein "defer" - noch ein "async" -Attribut haben. Die Kenntnis eines der möglichen ID / SRC / TITLE-Attribute des Skript-Tags könnte auch in diesen Fällen funktionieren. Die Vorschläge von Greg und Justin sind also richtig.

Es gibt bereits einen Vorschlag für eine document.currentScriptauf den WHATWG-Listen.

BEARBEITEN : Firefox> 4 implementiert diese sehr nützliche Eigenschaft bereits, ist jedoch in IE11 nicht verfügbar, das ich zuletzt überprüft habe, und nur in Chrome 29 und Safari 8 verfügbar.

BEARBEITEN : Niemand hat die Sammlung "document.scripts" erwähnt, aber ich glaube, dass das Folgende eine gute Cross-Browser-Alternative sein kann, um das aktuell ausgeführte Skript zu erhalten:

var me = document.scripts[document.scripts.length -1];
Diego Perini
quelle
1
Es ist document.scripts nicht document.script
Moritz
9

Hier ist eine Art Polyfill, die nutzt, document.CurrentScriptwenn sie vorhanden ist, und auf das Suchen des Skripts anhand der ID zurückgreift.

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

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

Wenn Sie dies oben in jedes Skript-Tag einfügen, können Sie meines Erachtens konsistent wissen, welches Skript-Tag ausgelöst wird, und Sie können das Skript-Tag auch im Kontext eines asynchronen Rückrufs referenzieren.

Ungetestet, hinterlassen Sie also Feedback für andere, wenn Sie es versuchen.

Kunstrecht
quelle
Das idAttribut ist jedoch in einem scriptElement ungültig . Welche Probleme könnte dieser Ansatz verursachen?
Nr.
1
@nr - Nein, alle Elemente können ein idAttribut haben. id, classUnd slotsind auf der DOM - Ebene, nicht die HTML - Ebene definiert. Wenn Sie zu den globalen Attributen in HTML gehen und über die Liste scrollen, finden Sie "DOM-Standard definiert die Benutzeragentenanforderungen für die Klassen-, ID- und Slot-Attribute für jedes Element in einem beliebigen Namespace." gefolgt von "Die Klassen-, ID- und Slot-Attribute können für alle HTML-Elemente angegeben werden." Die DOM-Spezifikation deckt dies hier ab .
TJ Crowder
6

Es muss beim Laden der Seite funktionieren und wenn ein Skript-Tag mit Javascript hinzugefügt wird (z. B. mit Ajax).

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

Befolgen Sie diese einfachen Schritte, um einen Verweis auf den aktuell ausgeführten Skriptblock zu erhalten:

  1. Fügen Sie eine zufällige eindeutige Zeichenfolge in den Skriptblock ein (muss in jedem Skriptblock eindeutig / unterschiedlich sein).
  2. Iterieren Sie das Ergebnis von document.getElementsByTagName ('script') und suchen Sie die eindeutige Zeichenfolge aus jedem ihrer Inhalte (abgerufen von der Eigenschaft innerText / textContent).

Beispiel (ABCDE345678 ist die eindeutige ID) :

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

Übrigens können Sie für Ihren Fall einfach die altmodische document.write () -Methode verwenden, um ein anderes Skript einzuschließen. Wie Sie bereits erwähnt haben, dass DOM noch nicht gerendert wurde, können Sie die Tatsache nutzen, dass der Browser das Skript immer in linearer Reihenfolge ausführt (mit Ausnahme des verzögerten Skripts, das später gerendert wird), sodass der Rest Ihres Dokuments immer noch "nicht vorhanden" ist. Alles, was Sie über document.write () schreiben, wird direkt nach dem Aufruferskript platziert.

Beispiel einer ursprünglichen HTML-Seite :

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

Inhalt von script.js :

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

Nach dem Rendern wird die DOM-Struktur zu:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY
Ferdinand Liu
quelle
Dies scheint nur für Inline-Skripte zu funktionieren, nicht für externe Skripte. Im letzteren Fall sind alle Eigenschaften innerText, text und textContent leer.
Jos de Jong
3

Ein Ansatz für den Umgang mit asynchronen und verzögerten Skripten besteht darin, den Onload-Handler zu nutzen. Legen Sie einen Onload-Handler für alle Skript-Tags fest, und der erste, der ausgeführt wird, sollte Ihnen gehören.

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
quelle
3

Um das Skript zu erhalten, das aktuell das Skript geladen hat, können Sie es verwenden

var thisScript = document.currentScript;

Sie müssen am Anfang Ihres Skripts eine Referenz aufbewahren, damit Sie später aufrufen können

var url = thisScript.src
Peter Lakatos - Appnomaden
quelle
2

Betrachten Sie diesen Algorithmus. Wenn Ihr Skript geladen wird (wenn mehrere identische Skripte vorhanden sind), durchsuchen Sie document.scripts, suchen Sie das erste Skript mit dem richtigen "src" -Attribut, speichern Sie es und markieren Sie es als "besucht" mit einem Datenattribut oder einem eindeutigen Klassennamen.

Wenn das nächste Skript geladen wird, durchsuchen Sie document.scripts erneut und übergeben Sie alle Skripte, die bereits als besucht markiert sind. Nehmen Sie die erste nicht besuchte Instanz dieses Skripts.

Dies setzt voraus, dass identische Skripte wahrscheinlich in der Reihenfolge ausgeführt werden, in der sie geladen werden, von Kopf zu Körper, von oben nach unten, von synchron zu asynchron.

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

Dadurch wird das gesamte Skript nach dem ersten übereinstimmenden Skript durchsucht, das nicht mit dem speziellen Attribut gekennzeichnet ist.

Markieren Sie dann diesen Knoten, falls gefunden, mit einem Datenattribut, damit nachfolgende Scans ihn nicht auswählen. Dies ähnelt den Graph-Traversal-BFS- und DFS-Algorithmen, bei denen Knoten als "besucht" markiert werden können, um ein erneutes Aufrufen zu verhindern.

LSOJ
quelle
Willkommen bei Stack Overflow. Möchten Sie dem Algorithmus Code hinzufügen?
Gary99
Thar you go, @ Gary99
LSOJ
0

Ich habe dies, das in FF3, IE6 und 7 funktioniert. Die Methoden in den bei Bedarf geladenen Skripten sind erst verfügbar, wenn das Laden der Seite abgeschlossen ist, aber dies ist immer noch sehr nützlich.

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

quelle
0

Wenn Sie den Dateinamen des Skripts annehmen können, können Sie ihn finden. Ich habe bisher nur die folgende Funktion in Firefox wirklich getestet.

  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');
Nur verliebt
quelle
1
Sehr veraltet. Dies kann mit dem querySelector erfolgen, einem einfachen Oneliner!
Fabian von Ellerts
-1

Ich habe festgestellt, dass der folgende Code am konsistentesten, performantesten und einfachsten ist.

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
quelle