Kann ich Javascript ausführen, bevor die gesamte Seite geladen wird?

Antworten:

185

Nicht nur können Sie, aber Sie müssen eine besondere Anstrengungen unternehmen , um nicht zu , wenn Sie nicht wollen. :-)

Wenn der Browser scriptbeim Parsen des HTML- Codes auf ein klassisches Tag stößt , stoppt er das Parsen und übergibt es dem JavaScript-Interpreter, der das Skript ausführt. Der Parser wird erst fortgesetzt, wenn die Skriptausführung abgeschlossen ist (da das Skript möglicherweise document.writeAufrufe zum Ausgeben von Markups ausführt, die der Parser verarbeiten soll).

Dies ist das Standardverhalten, aber Sie haben einige Optionen, um die Skriptausführung zu verzögern:

  1. Verwenden Sie JavaScript-Module. Ein type="module"Skript wird verschoben , bis das HTML vollständig analysiert und die ursprüngliche DOM erstellt wurde. Dies ist nicht der Hauptgrund für die Verwendung von Modulen, aber einer der Gründe:

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

    Der Code wird abgerufen (sofern er separat ist) und parallel zur HTML-Analyse analysiert, jedoch erst ausgeführt, wenn die HTML-Analyse abgeschlossen ist. (Wenn Ihr Modulcode nicht in einer eigenen Datei, sondern inline ist, wird er ebenfalls zurückgestellt, bis die HTML-Analyse abgeschlossen ist.)

    Dies war nicht verfügbar, als ich diese Antwort 2010 zum ersten Mal schrieb, aber hier im Jahr 2020 unterstützen alle wichtigen modernen Browser Module von Haus aus. Wenn Sie ältere Browser unterstützen müssen, können Sie Bundler wie Webpack und Rollup.js verwenden.

  2. Verwenden Sie das deferAttribut für ein klassisches Skript-Tag:

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

    Wie beim Modul wird der Code in my-code.jsparallel zum HTML-Parsing abgerufen und analysiert, jedoch erst ausgeführt, wenn das HTML-Parsing abgeschlossen ist. Funktioniertdefer jedoch nicht mit Inline-Skriptinhalten , sondern nur mit externen Dateien, auf die über verwiesen wird src.

  3. Ich glaube nicht, dass es das ist, was Sie wollen, aber Sie können das asyncAttribut verwenden, um den Browser anzuweisen, den JavaScript-Code parallel zur HTML-Analyse abzurufen, ihn dann aber so schnell wie möglich auszuführen, auch wenn die HTML-Analyse nicht abgeschlossen ist . Sie können es auf ein type="module"Tag setzen oder anstelle defereines klassischen scriptTags verwenden.

  4. Fügen Sie das scriptTag am Ende des Dokuments unmittelbar vor dem schließenden </body>Tag ein:

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

    Auf diese Weise sind alle durch den obigen HTML-Code definierten Elemente vorhanden und können verwendet werden, obwohl der Code ausgeführt wird, sobald er angetroffen wird.

    Früher verursachte dies bei einigen Browsern eine zusätzliche Verzögerung, da sie den Code erst abrufen konnten, wenn das scriptTag gefunden wurde. Moderne Browser scannen jedoch voraus und beginnen mit dem Vorabrufen. Dennoch ist dies zu diesem Zeitpunkt die dritte Wahl, beide Module und defersind bessere Optionen.

Die Spezifikation hat eine nützliche Diagramm einen rohen zeigt scriptTag, defer, async, type="module", und type="module" asyncund den Zeitpunkt , wenn der JavaScript - Code abgerufen und ausgeführt:

Geben Sie hier die Bildbeschreibung ein

Hier ist ein Beispiel für das Standardverhalten, ein Raw- 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>

(Siehe meine Antwort hier für Details zu diesem NodeListCode.)

Wenn Sie das ausführen, sehen Sie "Absatz 1" in Grün, aber "Absatz 2" ist schwarz, da das Skript synchron mit der HTML-Analyse ausgeführt wurde und daher nur den ersten Absatz gefunden hat, nicht den zweiten.

Im Gegensatz dazu ist hier ein type="module"Skript:

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

Beachten Sie, wie sie jetzt beide grün sind; Der Code wurde erst ausgeführt, nachdem die HTML-Analyse abgeschlossen war. Dies gilt auch für einen defer scriptmit externen Inhalten (jedoch nicht für Inline-Inhalte).

(Es gab keine Notwendigkeit für die NodeListKontrolle gibt , weil jeder modernen Browser Module bereits Unterstützung forEachan NodeList.)

In dieser modernen Welt gibt es keinen wirklichen Wert für das DOMContentLoadedEreignis der "Bereit" -Funktion, die PrototypeJS, jQuery, ExtJS, Dojo und die meisten anderen früher bereitstellten (und immer noch bereitstellen). Verwenden Sie einfach Module oder defer. Selbst damals gab es nicht viel Grund, sie zu verwenden (und sie wurden oft falsch verwendet, da sie die Seitenpräsentation aufhielten, während die gesamte jQuery-Bibliothek geladen wurde, weil sie scriptsich im headstatt nach dem Dokument befand), was einige Entwickler taten Google hat sich frühzeitig gemeldet. Dies war auch ein Grund für die YUI-Empfehlung , Skripte am Ende des body, wieder damals, zu platzieren.

TJ Crowder
quelle
1
@FeRD - Danke für die Bearbeitung. Kritiker lehnten es ab, aber Sie waren ganz richtig. :-)
TJ Crowder
Oder setzen Sie alles in eine Funktion und setzen Sie es ein <body onload="doIt()>".
Justin Liu
@JustinLiu - Das loadEreignis tritt sehr spät im Seitenladezyklus auf. Es ist fast nie empfehlenswert, bis dahin mit der Ausführung Ihres JavaScript zu warten. Aber ja, das ist eine Option. Ich habe die Antwort für 2020 übrigens überarbeitet.
TJ Crowder
Oder Sie könnten verwendendocument.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Justin Liu
@ JustinLiu - Ja, siehe den letzten Absatz. :-) Ich habe noch nie eine Notwendigkeit gesehen, aber du kannst.
TJ Crowder
6

Sie können jederzeit Javascript-Code ausführen. AFAIK wird ausgeführt, sobald der Browser das <script> -Tag erreicht, in dem es sich befindet. Sie können jedoch nicht auf Elemente zugreifen, die noch nicht geladen sind.

Wenn Sie also Zugriff auf Elemente benötigen, sollten Sie warten, bis das DOM geladen ist (dies bedeutet nicht, dass die gesamte Seite geladen ist, einschließlich Bilder und Material. Es ist nur die Struktur des Dokuments, die viel früher geladen wurde, sodass Sie normalerweise gewonnen haben keine Verzögerung bemerken), unter Verwendung des DOMContentLoadedEreignisses oder der Funktionen wie $.readyin jQuery.

Marian
quelle
2
Das DOMContentLoaded-Ereignis wird ausgelöst, sobald die DOM-Hierarchie vollständig erstellt wurde. Das Ladeereignis führt dies aus, wenn alle Bilder und Unterrahmen vollständig geladen wurden.
BeauCielBleu