Wie kann ich überprüfen, ob DOM ohne Framework bereit ist?

72

Die Frage ist so wie eine Unmenge anderer hier und im Internet - Wie kann man überprüfen, ob DOM in Javascript geladen wurde? Aber hier ist der Haken:

  • Ohne ein Framework wie jQuery usw.;
  • Ohne zu wissen, ob Ihr Skript über ein statisch platziertes <script>Tag oder über ein anderes Javascript viel später geladen wurde, nachdem das DOM bereits geladen wurde.

Kann dies mehr oder weniger zuverlässig und browserübergreifend erfolgen?

Hinzugefügt: Lassen Sie mich klarstellen: Ich schreibe eine eigenständige JS-Datei, die in beliebige Webseiten aufgenommen werden kann. Ich möchte Code ausführen, nachdem das DOM geladen wurde. Aber ich weiß nicht , WIE mein Skript aufgenommen werden. Dies kann durch Platzieren eines <script>Tags geschehen (in diesem Fall onloadfunktionieren die herkömmlichen Lösungen oder Lösungen für die DOM-Bereitschaft). oder es könnte über AJAX oder ein anderes Mittel geladen werden, viel später, nachdem das DOM bereits geladen wurde (so dass die zuvor erwähnten Lösungen niemals ausgelöst werden).

Vilx-
quelle
3
Die document.readyStateEigenschaft kann verwendet werden, obwohl ich nicht sicher bin, ob der Browser kompatibel ist ...
Digital Plane
1
Nicht sehr gut, fürchte ich. :(
Vilx
1
Sehen Sie sich dieses kleine Dienstprogramm an: github.com/cms/domready Es ist ungefähr ~ 0,5 KB klein.
Christian C. Salvadó
1
@ DigitalPlane - Ich nehme es zurück. Es hat verdammt gute Browser-Unterstützung! Füge es als Antwort hinzu und ich werde es akzeptieren.
Vilx

Antworten:

79

Mit der document.readyStateEigenschaft kann überprüft werden, ob das Dokument bereit ist. Von MDN:

Werte

Der readyState eines Dokuments kann einer der folgenden sein:

  • Laden - Das Dokument wird noch geladen.
  • interaktiv - Das Dokument wurde vollständig geladen und das Dokument wurde analysiert, aber Unterressourcen wie Bilder, Stylesheets und Frames werden noch geladen.
  • vollständig - Das Dokument und alle Unterressourcen wurden vollständig geladen. Der Status zeigt an, dass das Ladeereignis ausgelöst wird.

Codebeispiel:

if(document.readyState === "complete") {
    // Fully loaded!
}
else if(document.readyState === "interactive") {
    // DOM ready! Images, frames, and other subresources are still downloading.
}
else {
    // Loading still in progress.
    // To wait for it to complete, add "DOMContentLoaded" or "load" listeners.

    window.addEventListener("DOMContentLoaded", () => {
        // DOM ready! Images, frames, and other subresources are still downloading.
    });

    window.addEventListener("load", () => {
        // Fully loaded!
    });
}
Digitales Flugzeug
quelle
4
Denken Sie daran, dass addEventListener()mit IE9 hinzugefügt wurde, das für Windows XP nicht verfügbar ist
Christoph
1
Natürlich. Dies geht zusammen mit den anderen Lösungen für eine DOMReady-Ereignissimulation.
Vilx
7
Zum einen ist dies nicht browserübergreifend wirksam. Zweitens wartet dies darauf, dass auch Bilder geladen werden. Dies verhält sich wie window.onload.
Brian Sweat
5
Sollten Sie nicht auf ein Ereignis mit dem Namen loadanstatt hören onload?
Natevw
2
[Esoterica-Warnung] * Ich glaube, es wäre angemessener, document.readyState == "complete" anstelle von document.readyState === "complete" zu verwenden. * Je nachdem, was Ihr Code tun wird, ist es möglicherweise angemessener, zu warten bis alle Bilder geladen sind * Ist es nicht deprimierend, dass die Browserhersteller alle so kompromisslos inkompetent sind? Alles am Design von JavaScript ist definitiv falsch.
Debater
10

Firefox-, Opera- und Webkit-basierte Browser verfügen über ein Ereignis auf Dokumentebene, auf DOMContentLoadeddas Sie warten können document.addEventListener("DOMContentLoaded", fn, false).

Im IE ist es komplizierter. Im IE überwacht jQuery onreadystatechangedas Dokumentobjekt auf einen bestimmten Readystate mit einer Sicherung des Ereignisses document.onload. document.onload wird später ausgelöst, als das DOM bereit ist (nur wenn alle Bilder vollständig geladen wurden), sodass es nur als Backstop verwendet wird, wenn die früheren Ereignisse aus irgendeinem Grund nicht funktionieren.

Wenn Sie einige Zeit mit Googeln verbringen, finden Sie Code, um dies zu tun. Ich denke, der am besten überprüfte Code dafür ist in den großen Frameworks wie jQuery und YUI. Selbst wenn ich dieses Framework nicht verwende, suche ich in ihrem Quellcode nach Techniken.

Hier ist der Hauptteil der jQuery 1.6.2-Quelle für document.ready():

bindReady: function() {
    if ( readyList ) {
        return;
    }

    readyList = jQuery._Deferred();

    // Catch cases where $(document).ready() is called after the
    // browser event has already occurred.
    if ( document.readyState === "complete" ) {
        // Handle it asynchronously to allow scripts the opportunity to delay ready
        return setTimeout( jQuery.ready, 1 );
    }

    // Mozilla, Opera and webkit nightlies currently support this event
    if ( document.addEventListener ) {
        // Use the handy event callback
        document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

        // A fallback to window.onload, that will always work
        window.addEventListener( "load", jQuery.ready, false );

    // If IE event model is used
    } else if ( document.attachEvent ) {
        // ensure firing before onload,
        // maybe late but safe also for iframes
        document.attachEvent( "onreadystatechange", DOMContentLoaded );

        // A fallback to window.onload, that will always work
        window.attachEvent( "onload", jQuery.ready );

        // If IE and not a frame
        // continually check to see if the document is ready
        var toplevel = false;

        try {
            toplevel = window.frameElement == null;
        } catch(e) {}

        if ( document.documentElement.doScroll && toplevel ) {
            doScrollCheck();
        }
    }
},
jfriend00
quelle
1
Noch einmal - dies funktioniert nicht, wenn ich meinen Ereignishandler anhänge, nachdem das DOM bereits geladen wurde.
Vilx
4
Schauen Sie in die jQuery-Quelle, die ich aufgenommen habe. Deshalb haben sie die Zeile, mit der if ( document.readyState === "complete" ) {sie beginnen, damit sie den Fall behandeln können, in dem das Dokument bereits fertig ist. Wenn Sie dazu Ihr eigenes Mini-Framework erstellen, müssen Sie diese Art von Funktionalität selbst einbauen.
jfriend00
9

Wenn es in Ordnung document.readyStateist, sich auf eine schnelle und schmutzige Lösung mit Abfragen zu verlassen:

(function() {
    var state = document.readyState;
    if(state === 'interactive' || state === 'complete') {
        // do stuff
    }
    else setTimeout(arguments.callee, 100);
})();
Christoph
quelle
document.readyStateist die Antwort auf meine Probleme. Wie ich bereits mehrfach erwähnt habe, ist das Anhängen an ein "DOMReady" -Ereignis kein Problem. Das Problem ist, wenn der readyState == abgeschlossen ist, sodass das DOMReady-Ereignis niemals ausgelöst wird.
Vilx
@ Vilx-: Mein Punkt ist, dass, wenn Sie sich document.readyStatesowieso darauf verlassen (dh Sie möchten keine älteren Nicht-IE-Browser unterstützen), überhaupt kein browserspezifischer Code erforderlich ist ( DOMContentLoadedvs. onloadvs. onreadystatechange, addEventListenervs attachEvent). - Die obige Lösung funktioniert überall
Christoph
Soweit ich sehen kann, ist der einzige Browser, den ich nicht unterstütze, FireFox unter 3.6. Und um ehrlich zu sein, ist es ein Randfall (meistens wird das Skript auf traditionelle Weise aufgenommen). Wenn ich es also für eine sehr seltene Kombination vermisse, wird es keine so große Sache sein. : P
Vilx
8

Dies funktioniert für alle Browser und ist kurz und prägnant:

var execute = function () {
  alert("executing code");  
};

if ( !!(window.addEventListener) )
  window.addEventListener("DOMContentLoaded", execute)
else // MSIE
  window.attachEvent("onload", execute)
John Foley
quelle
4

Das DOMContentLoadedEreignis wird ausgelöst, wenn das ursprüngliche HTML-Dokument vollständig geladen und analysiert wurde, ohne darauf zu warten, dass Stylesheets, Bilder und Subframes vollständig geladen werden. Gut, Chrome, Firefox, IE9, Oper und Safari unterstützen es gleichermaßen

document.addEventListener("DOMContentLoaded", function(event) 
{
    console.log("DOM fully loaded and parsed");
}

ANMERKUNG: Internet Explorer 8 unterstützt das Ereignis readystatechange, mit dem erkannt werden kann, wann das DOM bereit ist.

Malik Khalil
quelle
2

Hier ist eine Möglichkeit, indem Sie das Skript unten auf der Seite ausführen. Darüber hinaus können Sie mithilfe von window.onload warten, bis alle Bilder / Skripte geladen sind. Oder Sie können einfach den Code unten platzieren, ohne darauf zu warten, dass Bilder geladen werden.

<html>
<head>
</head>
<body>
</body>
<script language="text/javascript">
  window.onload = (function (oldOnLoad) {
    return function () {
      if (oldOnLoad) { 
        olOnLoad();  //fire old Onload event that was attached if any.
      }
      // your code to run after images/scripts are loaded
    }
  })(window.onload);

  // your code to run after DOM is loaded
</script>
</html>

Bearbeitet: für Vilx 'Kommentar

Viele Onload-Bindungen hier sind ein Beispiel http://jsfiddle.net/uTF2N/3/

John Hartsock
quelle
+1. Vielleicht möchten Sie eine Erklärung hinzufügen, wie dies für Personen funktioniert, die später suchen und diese finden, die jedoch möglicherweise nicht viel Erfahrung haben.
Ken White
1
Ich denke, Sie verstoßen: "Ohne zu wissen, ob Ihr Skript über ein statisch platziertes <script> -Tag oder über ein anderes Javascript viel später geladen wurde, nachdem das DOM bereits geladen wurde".
Levi Morrison
Es tut mir leid, ich hatte nicht gesagt, dass ich über ein eigenständiges Skript spreche. Ich habe klargestellt.
Vilx
@ Levi Morrison. window.onload wird ausgeführt, nachdem alle Bilder / Skripte geladen wurden.
John Hartsock
In meinem Fall würde es mich nicht einmal so sehr interessieren, aber trotzdem gibt es viele DOM-Bereitschaftsskripte, so dass es kein Problem ist. Das Hauptproblem ist, dass ich nicht weiß, ob das Onload-Ereignis möglicherweise bereits vor langer Zeit ausgelöst wurde, sodass das Anhängen eines anderen Handlers keine Auswirkungen hat.
Vilx