Czy mogę uruchomić javascript przed załadowaniem całej strony?

Odpowiedzi:

189

Nie tylko może ci, ale trzeba dołożyć szczególnych starań nie do zrobienia, jeśli nie chcą. :-)

Gdy przeglądarka napotyka klasyczny scripttag podczas analizowania kodu HTML, zatrzymuje przetwarzanie i przekazuje go interpreterowi JavaScript, który uruchamia skrypt. Parser nie kontynuuje, dopóki wykonanie skryptu nie zostanie zakończone (ponieważ skrypt może document.writewywoływać znaczniki wyjściowe, które powinien obsługiwać parser).

To jest domyślne zachowanie, ale masz kilka opcji opóźnienia wykonania skryptu:

  1. Użyj modułów JavaScript. type="module"Skrypt jest odroczone do momentu HTML został w pełni analizowany i początkowy DOM tworzone. To nie jest główny powód używania modułów, ale jest to jeden z powodów:

    <script type="module" src="./my-code.js"></script>
    <!-- Or -->
    <script type="module">
    // Your code here
    </script>
    

    Kod zostanie pobrany (jeśli jest oddzielny) i przeanalizowany równolegle z analizą HTML, ale nie zostanie uruchomiony, dopóki nie zostanie zakończona analiza HTML. (Jeśli kod modułu jest wbudowany, a nie we własnym pliku, jest również odroczony do zakończenia analizowania kodu HTML).

    Nie było to dostępne, kiedy po raz pierwszy napisałem tę odpowiedź w 2010 roku, ale tutaj w 2020 roku wszystkie główne nowoczesne przeglądarki natywnie obsługują moduły, a jeśli potrzebujesz obsługiwać starsze przeglądarki, możesz użyć pakietów, takich jak Webpack i Rollup.js.

  2. Użyj deferatrybutu w klasycznym tagu skryptu:

    <script defer src="./my-code.js"></script>
    

    Podobnie jak w przypadku modułu, kod my-code.jszostanie pobrany i przeanalizowany równolegle z analizą HTML, ale nie zostanie uruchomiony, dopóki nie zostanie zakończona analiza HTML. Ale , defernie działa z inline treści skryptu, tylko z plików zewnętrznych poprzez odwołanie src.

  3. Nie sądzę, że to, co pan chce, ale można użyć asyncatrybutu poinformować przeglądarkę, aby pobrać kod JavaScript równolegle z parsowania HTML, ale następnie uruchomić go jak najszybciej, nawet jeśli parsowania HTML nie jest kompletna . Możesz umieścić go na type="module"tagu lub użyć go zamiast deferklasycznego scripttagu.

  4. Umieść scripttag na końcu dokumentu, tuż przed </body>tagiem zamykającym :

    <!doctype html>
    <html>
    <!-- ... -->
    <body>
    <!-- The document's HTML goes here -->
    <script type="module" src="./my-code.js"></script><!-- Or inline script -->
    </body>
    </html>
    

    W ten sposób, nawet jeśli kod jest uruchamiany natychmiast po jego napotkaniu, wszystkie elementy zdefiniowane w HTML powyżej istnieją i są gotowe do użycia.

    Kiedyś powodowało to dodatkowe opóźnienie w niektórych przeglądarkach, ponieważ nie zaczęły one pobierać kodu, dopóki scriptnie napotkano tagu, ale nowoczesne przeglądarki skanują do przodu i rozpoczynają pobieranie wstępne. Mimo to jest to trzeci wybór w tym momencie, oba moduły i defersą lepszymi opcjami.

Spec jest użyteczny schemat przedstawiający surowego scripttag defer, async, type="module"i type="module" asynci taktowanie, gdy kod JavaScript jest pobrana i prowadzony:

wprowadź opis obrazu tutaj

Oto przykład domyślnego zachowania, surowy scripttag:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script>
    if (typeof NodeList !== "undefined" && !NodeList.prototype.forEach) {
        NodeList.prototype.forEach = Array.prototype.forEach;
    }
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

(Zobacz moją odpowiedź tutaj, aby uzyskać szczegółowe informacje na temat tego NodeListkodu).

Po uruchomieniu tego zobaczysz „Akapit 1” na zielono, ale „Akapit 2” jest czarny, ponieważ skrypt działał synchronicznie z analizą kodu HTML, więc znalazł tylko pierwszy akapit, a nie drugi.

W przeciwieństwie do tego, oto type="module"skrypt:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script type="module">
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

Zwróć uwagę, że oboje są teraz zielone; kod nie działał, dopóki nie zakończyła się analiza HTML. Byłoby to również prawdą w przypadku elementu defer scriptz treścią zewnętrzną (ale nie z treścią wbudowaną).

(Nie było potrzeby NodeListsprawdzania w tym miejscu, ponieważ każda nowoczesna przeglądarka obsługująca moduły już ma forEachwłączone NodeList.)

We współczesnym świecie nie ma prawdziwej wartości w DOMContentLoadedprzypadku „gotowej” funkcji, którą PrototypeJS, jQuery, ExtJS, Dojo i większość innych dostarczały w ciągu dnia (i nadal zapewniają); po prostu użyj modułów lub defer. Nawet w tamtych czasach nie było zbyt wiele powodów, aby ich używać (i często były używane nieprawidłowo, wstrzymując prezentację strony, podczas gdy cała biblioteka jQuery była ładowana, ponieważ scriptznajdowała się w headdokumencie zamiast za), coś, co niektórzy programiści Google zgłosił się wcześnie. To również był jeden z powodów, dla których YUI zalecił umieszczenie skryptów na końcu bodytego dnia.

TJ Crowder
źródło
Lub umieść to wszystko w funkcji i wstaw to <body onload="doIt()>".
Justin Liu
@JustinLiu - loadZdarzenie ma miejsce bardzo późno w cyklu ładowania strony, prawie nigdy nie jest najlepszą praktyką czekanie z uruchomieniem JavaScript do tego czasu. Ale tak, jest to opcja. Przy okazji, przerobiłem odpowiedź na rok 2020.
TJ Crowder
Lub możesz użyćdocument.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Justin Liu
@JustinLiu - Tak, patrz ostatni akapit. :-) Nigdy nie widziałem takiej potrzeby, ale możesz.
TJ Crowder
@JustinLiu - To zaczyna być przesada. Jeśli chcesz opublikować odpowiedź, napisz odpowiedź.
TJ Crowder
6

Możesz uruchomić kod javascript w dowolnym momencie. AFAIK jest to wykonywane w momencie, gdy przeglądarka osiągnie znacznik <script>, w którym się znajduje. Ale nie masz dostępu do elementów, które nie zostały jeszcze załadowane.

Więc jeśli potrzebujesz dostępu do elementów, powinieneś poczekać, aż DOM zostanie załadowany (nie oznacza to, że załadowana zostanie cała strona, w tym obrazy i inne rzeczy. To tylko struktura dokumentu, który jest ładowany znacznie wcześniej, więc zwykle wygrywasz nie zauważyłem opóźnienia), używając DOMContentLoadedzdarzenia lub funkcji takich jak $.readyw jQuery.

Marian
źródło
2
Zdarzenie DOMContentLoaded zostanie uruchomione, gdy tylko hierarchia DOM zostanie w pełni skonstruowana, a zdarzenie load zrobi to, gdy wszystkie obrazy i ramki podrzędne zostaną wczytane.
BeauCielBleu