Jak mogę odwołać się do znacznika script, który załadował aktualnie wykonywany skrypt?

303

Jak mogę odwołać się do elementu skryptu, który załadował aktualnie uruchomiony skrypt javascript?

Oto sytuacja. Skrypt „master” jest ładowany wysoko na stronie, najpierw pod znacznikiem 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>

W „scripts.js” znajduje się skrypt, który musi umożliwiać ładowanie innych skryptów na żądanie. Normalna metoda nie do końca działa, ponieważ muszę dodać nowe skrypty bez odwoływania się do znacznika HEAD, ponieważ element HEAD nie zakończył renderowania:

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

Chcę odwołać się do elementu skryptu, który załadował bieżący skrypt, aby móc następnie dołączyć nowe dynamicznie ładowane tagi skryptu do DOM po nim.

<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
źródło
1
Słowo ostrzeżenia: modyfikacja DOM podczas jego ładowania spowoduje świat cierpienia w IE6 i IE7 . Lepiej będzie uruchomić ten kod po załadowaniu strony.
Tryptyk
6
Wygląda na to, że jest już na caniuse: caniuse.com/#feat=document-currentscript
Tyler

Odpowiedzi:

653

Jak uzyskać bieżący element skryptu:

1. Użyj document.currentScript

document.currentScriptzwróci <script>element, którego skrypt jest aktualnie przetwarzany.

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

Korzyści

  • Prosty i wyraźny. Niezawodny.
  • Nie trzeba modyfikować tagu skryptu
  • Działa ze skryptami asynchronicznymi ( deferi async)
  • Działa ze skryptami wstawianymi dynamicznie

Problemy

  • Nie będzie działać w starszych przeglądarkach i IE.
  • Nie działa z modułami <script type="module">

2. Wybierz skrypt według identyfikatora

Nadanie skryptowi atrybutu id pozwoli ci łatwo wybrać go według identyfikatora z poziomu jego użycia document.getElementById().

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

Korzyści

  • Prosty i wyraźny. Niezawodny.
  • Prawie powszechnie obsługiwane
  • Działa ze skryptami asynchronicznymi ( deferi async)
  • Działa ze skryptami wstawianymi dynamicznie

Problemy

  • Wymaga dodania niestandardowego atrybutu do znacznika script
  • id atrybut może powodować dziwne zachowanie skryptów w niektórych przeglądarkach w niektórych przypadkach krawędzi

3. Wybierz skrypt za pomocą data-*atrybutu

Nadanie skryptu data-*atrybutu pozwoli Ci łatwo wybrać go od wewnątrz.

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

Ma to kilka zalet w porównaniu z poprzednią opcją.

Korzyści

  • Prosty i wyraźny.
  • Działa ze skryptami asynchronicznymi ( deferi async)
  • Działa ze skryptami wstawianymi dynamicznie

Problemy

  • Wymaga dodania niestandardowego atrybutu do znacznika script
  • HTML5 i querySelector()niezgodny we wszystkich przeglądarkach
  • Mniej powszechnie obsługiwany niż przy użyciu tego idatrybutu
  • Poradzi sobie <script>z idprzypadkowymi skrzyniami.
  • Może się mylić, jeśli inny element ma ten sam atrybut danych i wartość na stronie.

4. Wybierz skrypt według src

Zamiast używać atrybutów danych, możesz użyć selektora, aby wybrać skrypt według źródła:

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

W embed.js:

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

Korzyści

  • Niezawodny
  • Działa ze skryptami asynchronicznymi ( deferi async)
  • Działa ze skryptami wstawianymi dynamicznie
  • Nie są wymagane niestandardowe atrybuty ani identyfikator

Problemy

  • Czy nie działają dla skryptów lokalnych
  • Powoduje problemy w różnych środowiskach, takich jak rozwój i produkcja
  • Statyczny i delikatny. Zmiana położenia pliku skryptu będzie wymagać modyfikacji skryptu
  • Mniej powszechnie obsługiwany niż przy użyciu tego idatrybutu
  • Powoduje problemy, jeśli załadujesz dwukrotnie ten sam skrypt

5. Pętlę nad wszystkimi skryptami, aby znaleźć ten, który chcesz

Możemy również zapętlić każdy element skryptu i sprawdzić każdy z osobna, aby wybrać ten, który chcemy:

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

To pozwala nam używać obu poprzednich technik w starszych przeglądarkach, które nie obsługują querySelector()dobrze atrybutów. Na przykład:

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

Dziedziczy to korzyści i problemy wynikające z zastosowanego podejścia, ale nie zależy od tego, querySelector()więc będzie działać w starszych przeglądarkach.

6. Pobierz ostatni wykonany skrypt

Ponieważ skrypty są wykonywane sekwencyjnie, ostatnim elementem skryptu bardzo często będzie aktualnie uruchomiony skrypt:

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

Korzyści

  • Prosty.
  • Prawie powszechnie obsługiwane
  • Nie są wymagane niestandardowe atrybuty ani identyfikator

Problemy

  • Czy nie działa ze skryptami asynchronicznymi ( defer& async)
  • Czy nie działa ze skryptami wstawionych dynamicznie
brice
źródło
4
To powinna być odpowiedź.
Royi Namir
1
Zgadzam się z @RoyiNamir. To najlepsza odpowiedź.
Hans
27
Dzięki chłopaki, ale wiesz, że odpowiedziałem 4 lata po zaakceptowanej odpowiedzi, prawda :)
brice
5
„document.currentScript” nie działa dla mnie z dynamicznie ładowanymi skryptami, zwraca wartość null w najnowszym chrome / firefox, „ostatnio wykonany skrypt” działa dobrze
xwild 12.02.16
1
nie działa, gdy scriptjest templatewstawiony do DOM
DOMu
86

Ponieważ skrypty są wykonywane sekwencyjnie, aktualnie wykonywany tag skryptu jest zawsze ostatnim tagiem skryptu na stronie do tego czasu. Aby uzyskać tag skryptu, możesz:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
Kawałek kawy
źródło
3
To jest proste i eleganckie. Przykładem tego jest nowy interfejs API Google Charts / Visualizations, jeśli rozpakujesz javascript. Ładują
Jason Thrasher
1
To świetny pomysł i zwykle działa dla mnie. Ale powinienem dodać, że zdarzają się chwile, kiedy zwracają odwołanie do innego skryptu. Nie jestem pewien, dlaczego - nie byłem w stanie tego wyśledzić. W związku z tym zwykle wybieram inną metodę, np. Zapisuję na stałe nazwę pliku skryptu i szukam znacznika skryptu o tej nazwie.
Ken Smith
53
Jednym z przykładów, które mogę wymyślić, gdzie może to zwrócić nieprawidłowe wyniki, jest asynchroniczne dodawanie znacznika skryptu do DOM.
Ugryzienie kawy
9
Tak, może to mieć nieprzewidywalne wyniki, więc możesz spróbować użyć selektora: $ ('script [src * = "/ mysource.js"]') ???
Jason Sebring
7
Nie działa, gdy masz załadowane skrypty po załadowaniu strony. Prawdopodobnie nie dostaniesz odpowiedniego tagu.
ThemeZ
11

Prawdopodobnie najłatwiej byłoby nadać idatrybutowi scrip tag .

Greg
źródło
2
Chociaż masz rację, jest wiele przypadków, w których pytanie PO jest prawidłowe, kilka z nich to: 1) podczas indeksowania 2) podczas pracy z DOM klienta, a on nie chce się zmieniać
nichochar
10

Skrypty są wykonywane sekwencyjnie tylko wtedy, gdy nie mają atrybutu „odroczenia” ani „asynchronizacji”. Znajomość jednego z możliwych atrybutów ID / SRC / TITLE znacznika skryptu może również działać w takich przypadkach. Tak więc sugestie Grega i Justina są poprawne.

Jest już propozycja document.currentScriptna liście WHATWG.

EDYCJA : Firefox> 4 już zaimplementował tę bardzo przydatną właściwość, ale nie jest ona dostępna w IE11 ostatnio sprawdziłem i dostępna tylko w Chrome 29 i Safari 8.

EDYCJA : Nikt nie wspomniał o kolekcji „document.scripts”, ale uważam, że poniższe mogą być dobrą alternatywą dla różnych przeglądarek, aby uzyskać aktualnie uruchomiony skrypt:

var me = document.scripts[document.scripts.length -1];
Diego Perini
źródło
1
To document.scripts, a nie document.script
Moritz
9

Oto odrobina wypełnienia, które wykorzystuje, document.CurrentScriptjeśli istnieje i powraca do znalezienia skryptu według identyfikatora.

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

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

Jeśli umieścisz to u góry każdego tagu skryptu, uważam, że będziesz w stanie konsekwentnie wiedzieć, który tag skryptu jest uruchamiany, a także będziesz mógł odwoływać się do tagu skryptu w kontekście asynchronicznego wywołania zwrotnego.

Nie przetestowano, więc jeśli spróbujesz, zostaw opinię innym.

Art Lawry
źródło
idAtrybutem jest nieważny w scriptelemencie chociaż. Jakie problemy może powodować to podejście?
nr
1
@nr - Nie, wszystkie elementy mogą mieć idatrybut. id, classi slotsą zdefiniowane na poziomie DOM, a nie na poziomie HTML. Jeśli przejdziesz do globalnych atrybutów w HTML i przewiniesz listę, zobaczysz, że „DOM DOM określa wymagania agenta użytkownika dla atrybutów klasy, identyfikatora i boksu dla dowolnego elementu w dowolnej przestrzeni nazw”. po którym następuje „Atrybuty klasy, identyfikatora i boksu mogą być określone we wszystkich elementach HTML”. Specyfikacja DOM obejmuje to tutaj .
TJ Crowder
6

Musi działać przy ładowaniu strony i po dodaniu znacznika skryptu z javascript (np. Z ajax)

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
David Callizaya
źródło
3

Wykonaj te proste kroki, aby uzyskać odniesienie do bieżącego bloku skryptu wykonującego:

  1. Umieść losowy unikalny ciąg w bloku skryptu (musi być unikalny / inny w każdym bloku skryptu)
  2. Iteruj wynik document.getElementsByTagName („skrypt”), szukając unikalnego ciągu znaków z każdej ich zawartości (uzyskanej z właściwości innerText / textContent).

Przykład (ABCDE345678 jest unikalnym identyfikatorem) :

<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, w twoim przypadku, możesz po prostu użyć starej metody document.write (), aby dołączyć inny skrypt. Jak wspomniałeś, że DOM nie jest jeszcze renderowany, możesz skorzystać z faktu, że przeglądarka zawsze wykonuje skrypt w kolejności liniowej (z wyjątkiem odroczonego, który zostanie wyrenderowany później), więc reszta twojego dokumentu nadal „nie istnieje”. Wszystko, co napiszesz za pomocą document.write (), zostanie umieszczone zaraz po skrypcie wywołującym.

Przykład oryginalnej strony HTML :

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

Treść script.js :

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

Po wyrenderowaniu struktura DOM stanie się:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY
Ferdinand Liu
źródło
Wydaje się, że działa to tylko w przypadku skryptów wbudowanych, a nie w przypadku skryptów zewnętrznych. W drugim przypadku wszystkie właściwości innerText, text i textContent są puste.
Jos de Jong
3

Podejście do radzenia sobie ze skryptami asynchronicznymi i odroczonymi polega na wykorzystaniu modułu obsługi obciążenia - ustaw moduł obsługi obciążenia dla wszystkich znaczników skryptu, a pierwszy, który wykonuje, powinien być twój.

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
źródło
3

Aby uzyskać skrypt, który aktualnie załadował skrypt, możesz użyć

var thisScript = document.currentScript;

Musisz zachować odniesienie na początku skryptu, aby móc później zadzwonić

var url = thisScript.src
Peter Lakatos - appnomads
źródło
2

Rozważ ten algorytm. Gdy skrypt zostanie załadowany (jeśli istnieje wiele identycznych skryptów), przejrzyj document.scripts, znajdź pierwszy skrypt z poprawnym atrybutem „src”, a następnie zapisz go i oznacz jako „odwiedzony” atrybutem danych lub unikalną nazwą klasy.

Po załadowaniu następnego skryptu ponownie przejrzyj skrypty document.scripts, pomijając każdy skrypt już oznaczony jako odwiedzony. Weź pierwszą niezapowiedzianą instancję tego skryptu.

Zakłada się, że identyczne skrypty prawdopodobnie będą wykonywane w kolejności, w jakiej są ładowane, od głowy do ciała, od góry do dołu, od synchronicznego do asynchronicznego.

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

Spowoduje to skanowanie całego skryptu w poszukiwaniu pierwszego pasującego skryptu, który nie jest oznaczony specjalnym atrybutem.

Następnie zaznacz ten węzeł, jeśli został znaleziony, atrybutem danych, aby kolejne skany go nie wybrały. Jest to podobne do algorytmów przechodzenia przez graf BFS i DFS, w których węzły można oznaczyć jako „odwiedzone”, aby zapobiec ponownej wizycie.

LSOJ
źródło
Witamy w Stack Overflow. Czy chciałbyś dołączyć jakiś kod do algorytmu?
Gary99
Thar you, @ Gary99
LSOJ
0

Mam to, które działa w FF3, IE6 i 7. Metody w skryptach ładowanych na żądanie nie są dostępne, dopóki ładowanie strony nie zostanie zakończone, ale nadal jest to bardzo przydatne.

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

źródło
0

Jeśli możesz założyć nazwę pliku skryptu, możesz go znaleźć. Do tej pory testowałem tylko następującą funkcję w przeglądarce 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');
Po prostu zakochany
źródło
1
Bardzo nieaktualne. Można to zrobić za pomocą querySelector, prostego narzędzia oneliner!
Fabian von Ellerts,
-1

Uważam, że poniższy kod jest najbardziej spójny, wydajny i najprostszy.

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
źródło