Reines JavaScript-Äquivalent von jQuery's $.ready() - wie man eine Funktion aufruft, wenn die Seite/DOM bereit dafür ist

Okay, dies könnte nur eine dumme Frage sein, obwohl ich bin sicher, es gibt viele andere Menschen, die die gleiche Frage von Zeit zu Zeit. Ich, ich möchte nur 100% sicher über es so oder so zu machen. Mit jQuery kennen wir alle die wunderbare

$('document').ready(function(){});

Angenommen, ich möchte eine Funktion ausführen, die in Standard-JavaScript geschrieben ist, ohne dass sie von einer Bibliothek unterstützt wird, und ich möchte eine Funktion starten, sobald die Seite bereit ist, sie zu verarbeiten. Was ist der richtige Weg, um dies zu tun?

Ich weiß, dass ich das kann:

window.onload="myFunction()";

...oder ich kann den "body"-Tag verwenden:

<body onload="myFunction()">

...oder ich kann sogar versuchen, am Ende der Seite nach allem, aber dem Ende des body oder html Tags wie:

<script type="text/javascript">
   myFunction();
</script>

Was ist eine Cross-Browser (alt/neu) konforme Methode, um eine oder mehrere Funktionen in einer Art und Weise wie jQuery's $.ready() auszuführen?

Lösung

In Ermangelung eines Frameworks, das die Cross-Browser-Kompatibilität für Sie übernimmt, ist es am einfachsten, am Ende des Body einen Aufruf an Ihren Code zu setzen. Dies ist schneller als ein "onload"-Handler, da dieser nur darauf wartet, dass das DOM bereit ist, und nicht darauf, dass alle Bilder geladen werden. Außerdem funktioniert dies in jedem Browser.






Your HTML here

<script>
// self executing function here
(function() {
   // your page initialization code here
   // the DOM will be available here

})();
</script>


Für moderne Browser (alles ab IE9 und neuer und jede Version von Chrome, Firefox oder Safari), wenn Sie in der Lage sein wollen, eine jQuery-ähnliche $(document).ready()-Methode zu implementieren, die Sie von überall aus aufrufen können (ohne sich Gedanken darüber zu machen, wo das aufrufende Skript positioniert ist), können Sie einfach etwas wie dieses verwenden:

function docReady(fn) {
    // see if DOM is already available
    if (document.readyState === "complete" || document.readyState === "interactive") {
        // call on next available tick
        setTimeout(fn, 1);
    } else {
        document.addEventListener("DOMContentLoaded", fn);
    }
}    

Verwendung:

docReady(function() {
    // DOM is loaded and ready for manipulation here
});

Wenn Sie volle Cross-Browser-Kompatibilität benötigen (einschließlich alter IE-Versionen) und nicht auf window.onload warten wollen, dann sollten Sie sich ansehen, wie ein Framework wie jQuery seine Methode $(document).ready() implementiert. Das ist ziemlich kompliziert und hängt von den Fähigkeiten des Browsers ab.

Um Ihnen eine kleine Vorstellung davon zu geben, was jQuery tut (was überall funktioniert, wo das Skript-Tag platziert ist).

Wenn es unterstützt wird, versucht es den Standard:

document.addEventListener('DOMContentLoaded', fn, false);

mit einem Fallback zu:

window.addEventListener('load', fn, false )

oder für ältere Versionen des IE, verwendet es:

document.attachEvent("onreadystatechange", fn);

mit einem Fallback zu:

window.attachEvent("onload", fn);

Und es gibt einige Workarounds im IE-Codepfad, denen ich nicht ganz folgen kann, aber es sieht so aus, als hätte es etwas mit Frames zu tun.


Hier ist ein vollständiger Ersatz für jQuery's .ready() geschrieben in plain javascript:

(function(funcName, baseObj) {
    // The public function name defaults to window.docReady
    // but you can pass in your own object and own function name and those will be used
    // if you want to put them in a different namespace
    funcName = funcName || "docReady";
    baseObj = baseObj || window;
    var readyList = [];
    var readyFired = false;
    var readyEventHandlersInstalled = false;

    // call this when the document is ready
    // this function protects itself against being called more than once
    function ready() {
        if (!readyFired) {
            // this must be set to true before we start calling callbacks
            readyFired = true;
            for (var i = 0; i < readyList.length; i++) {
                // if a callback here happens to add new ready handlers,
                // the docReady() function will see that it already fired
                // and will schedule the callback to run right after
                // this event loop finishes so all handlers will still execute
                // in order and no new ones will be added to the readyList
                // while we are processing the list
                readyList[i].fn.call(window, readyList[i].ctx);
            }
            // allow any closures held by these functions to free
            readyList = [];
        }
    }

    function readyStateChange() {
        if ( document.readyState === "complete" ) {
            ready();
        }
    }

    // This is the one public interface
    // docReady(fn, context);
    // the context argument is optional - if present, it will be passed
    // as an argument to the callback
    baseObj[funcName] = function(callback, context) {
        if (typeof callback !== "function") {
            throw new TypeError("callback for docReady(fn) must be a function");
        }
        // if ready has already fired, then just schedule the callback
        // to fire asynchronously, but right away
        if (readyFired) {
            setTimeout(function() {callback(context);}, 1);
            return;
        } else {
            // add the function and context to the list
            readyList.push({fn: callback, ctx: context});
        }
        // if document already ready to go, schedule the ready function to run
        if (document.readyState === "complete") {
            setTimeout(ready, 1);
        } else if (!readyEventHandlersInstalled) {
            // otherwise if we don't have event handlers installed, install them
            if (document.addEventListener) {
                // first choice is DOMContentLoaded event
                document.addEventListener("DOMContentLoaded", ready, false);
                // backup is window load event
                window.addEventListener("load", ready, false);
            } else {
                // must be IE
                document.attachEvent("onreadystatechange", readyStateChange);
                window.attachEvent("onload", ready);
            }
            readyEventHandlersInstalled = true;
        }
    }
})("docReady", window);

Die neueste Version des Codes ist öffentlich auf GitHub unter https://github.com/jfriend00/docReady verfügbar.

Verwendung:

// pass a function reference
docReady(fn);

// use an anonymous function
docReady(function() {
    // code here
});

// pass a function reference and a context
// the context will be passed to the function as the first argument
docReady(fn, context);

// use an anonymous function with a context
docReady(function(context) {
    // code here that can use the context argument that was passed to docReady
}, ctx);

Dies wurde getestet in:

IE6 and up
Firefox 3.6 and up
Chrome 14 and up
Safari 5.1 and up
Opera 11.6 and up
Multiple iOS devices
Multiple Android devices

Arbeitsimplementierung und Prüfstand: http://jsfiddle.net/jfriend00/YfD3C/


Hier ist eine Zusammenfassung, wie es funktioniert:

  1. Erstellen Sie einen IIFE (sofort aufgerufener Funktionsausdruck), damit wir nicht-öffentliche Statusvariablen haben können.
  2. Deklariere eine öffentliche Funktion docReady(fn, context)
  3. Wenn docReady(fn, context) aufgerufen wird, prüfen Sie, ob der ready handler bereits ausgelöst wurde. Wenn ja, planen Sie einfach den neu hinzugefügten Callback so, dass er direkt nach Beendigung dieses Threads von JS mit setTimeout(fn, 1) ausgelöst wird.
  4. Wenn der Ready-Handler noch nicht ausgelöst wurde, fügen Sie diesen neuen Callback zur Liste der Callbacks hinzu, die später aufgerufen werden sollen.
  5. Prüfen Sie, ob das Dokument bereits bereit ist. Wenn ja, führe alle Ready-Handler aus.
  6. Wenn wir noch keine Ereignis-Listener installiert haben, um zu wissen, wann das Dokument bereit ist, dann installieren Sie sie jetzt.
  7. Wenn document.addEventListener existiert, dann installiere Ereignis-Handler mit .addEventListener() für beide "DOMContentLoaded" und "load" Ereignisse. Das "load"-Ereignis ist ein Backup-Ereignis zur Sicherheit und sollte nicht benötigt werden.
  8. Wenn document.addEventListener nicht existiert, dann installieren Sie Event-Handler mit .attachEvent() für "onreadystatechange" und "onload" Events.
  9. Im onreadystatechange-Ereignis prüfen, ob der document.readyState === "complete" ist und wenn ja, eine Funktion aufrufen, um alle ready-Handler auszulösen.
  10. Rufen Sie in allen anderen Ereignishandlern eine Funktion auf, um alle Bereitschaftshandler auszulösen.
  11. In der Funktion, die alle Ready-Handler aufruft, überprüfe eine Statusvariable, um zu sehen, ob wir bereits gefeuert haben. Wenn ja, tun Sie nichts. Wenn wir noch nicht aufgerufen wurden, durchlaufen Sie das Array der Bereitschaftsfunktionen und rufen jede einzelne in der Reihenfolge auf, in der sie hinzugefügt wurden. Setzen Sie ein Flag, das anzeigt, dass alle Funktionen bereits aufgerufen wurden, damit sie nur einmal ausgeführt werden.
  12. Leeren Sie das Funktionsarray, damit alle Closures, die sie möglicherweise verwenden, wieder freigegeben werden können.

Handler, die mit docReady() registriert wurden, werden garantiert in der Reihenfolge abgefeuert, in der sie registriert wurden.

Wenn Sie docReady(fn) aufrufen, nachdem das Dokument bereits fertig ist, wird der Callback so geplant, dass er ausgeführt wird, sobald der aktuelle Thread der Ausführung mit setTimeout(fn, 1) beendet ist. Dies erlaubt es dem aufrufenden Code, immer davon auszugehen, dass es sich um asynchrone Rückrufe handelt, die später aufgerufen werden, auch wenn später ist, sobald der aktuelle Thread von JS beendet ist, und es bewahrt die Aufrufreihenfolge.

Kommentare (16)

Ihre Methode (Platzierung des Skripts vor dem schließenden Body-Tag)

<script>
   myFunction()
</script>

ist eine zuverlässige Methode, um alte und neue Browser zu unterstützen.

Kommentare (2)

Die Option document.ondomcontentready=function(){} sollte ausreichen, ist aber nicht vollständig browserfähig.

Scheint, als sollten Sie nur jQuery min verwenden

Kommentare (5)