Zuiver JavaScript equivalent van jQuery's $.ready() - hoe roep je een functie aan wanneer de pagina/DOM er klaar voor is

Oké, dit is misschien gewoon een domme vraag, hoewel ik er zeker van ben dat er genoeg andere mensen zijn die af en toe dezelfde vraag stellen. Ik, ik wil er gewoon 100% zeker van zijn, hoe dan ook. Met jQuery kennen we allemaal het prachtige

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

Maar laten's zeggen dat ik een functie wil uitvoeren die geschreven is in standaard JavaScript zonder dat er een bibliotheek achter zit, en dat ik een functie wil starten zodra de pagina klaar is om het aan te kunnen. Wat's de juiste manier om dit te benaderen?

Ik weet dat ik kan doen:

window.onload="myFunction()";

...of ik kan de body tag gebruiken:

<body onload="myFunction()">

...of ik kan zelfs proberen onderaan de pagina na alles, maar het einde body of html tag zoals:

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

Wat is een cross-browser(oud/nieuw)-conforme methode om een of meer functies uit te geven op een manier zoals jQuery's $.ready()?

Oplossing

Het eenvoudigste om te doen bij gebrek aan een framework dat alle cross-browser compatibiliteit voor je doet is om gewoon een oproep naar je code aan het einde van de body te zetten. Dit is sneller uit te voeren dan een onload handler omdat deze alleen wacht tot het DOM klaar is, niet tot alle afbeeldingen geladen zijn. En, dit werkt in elke browser.






Your HTML here

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

})();
</script>


Voor moderne browsers (alles vanaf IE9 en nieuwer en elke versie van Chrome, Firefox of Safari), als je in staat wilt zijn om een jQuery-achtige $(document).ready() methode te implementeren die je overal vandaan kunt aanroepen (zonder je zorgen te maken over waar het aanroepende script is gepositioneerd), kun je gewoon iets als dit gebruiken:

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

Gebruik:

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

Als je volledige cross browser compatibiliteit nodig hebt (inclusief oude versies van IE) en je wilt niet wachten op window.onload, dan moet je waarschijnlijk gaan kijken naar hoe een framework als jQuery zijn $(document).ready() methode implementeert. Het is vrij ingewikkeld, afhankelijk van de mogelijkheden van de browser.

Om je een beetje een idee te geven wat jQuery doet (wat overal zal werken waar de script tag is geplaatst).

Indien ondersteund, probeert het de standaard:

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

met een fallback naar:

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

of voor oudere versies van IE, gebruikt het:

document.attachEvent("onreadystatechange", fn);

met een fallback naar:

window.attachEvent("onload", fn);

En, er zijn wat work-arounds in het IE code pad dat ik niet helemaal volg, maar het lijkt erop dat het iets te maken heeft met frames.


Hier is een volledige vervanger voor jQuery's .ready() geschreven in gewoon 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);

De laatste versie van de code is publiekelijk gedeeld op GitHub op https://github.com/jfriend00/docReady

Gebruik:

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

Dit is getest 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

Werkende implementatie en testbed: http://jsfiddle.net/jfriend00/YfD3C/


Hier's een samenvatting van hoe het werkt:

  1. Maak een IIFE (immediately invoked function expression) zodat we niet-publieke toestandsvariabelen kunnen hebben.
  2. Verklaar een publieke functie docReady(fn, context).
  3. Wanneer docReady(fn, context) wordt aangeroepen, controleer dan of de ready handler al gevuurd heeft. Als dat zo is, plan dan de nieuw toegevoegde callback om te vuren direct nadat deze thread van JS is afgelopen met setTimeout(fn, 1).
  4. Als de ready handler nog niet gevuurd heeft, voeg dan deze nieuwe callback toe aan de lijst van callbacks die later aangeroepen moeten worden.
  5. Controleer of het document al klaar is. Zo ja, voer dan alle ready handlers uit.
  6. Als we nog'geen event listeners hebben geïnstalleerd om te weten wanneer het document klaar wordt, installeer ze dan nu.
  7. Als document.addEventListener bestaat, installeer dan event handlers met .addEventListener() voor zowel "DOMContentLoaded" als "load" events. De `"load" is een backup event voor de veiligheid en zou niet nodig moeten zijn.
  8. Als document.addEventListener niet bestaat', installeer dan event handlers met behulp van.attachEvent()voor"onreadystatechange"en"onload"` events.
  9. In de onreadystatechange gebeurtenis, controleer of de document.readyState === "complete" en zo ja, roep een functie aan om alle ready handlers af te vuren.
  10. Roep in alle andere event handlers een functie aan om alle ready handlers af te vuren.
  11. In de functie om alle ready handlers aan te roepen, controleer een state variabele om te zien of we'al gevuurd hebben. Als dat zo is, doe dan niets. Als we nog niet zijn opgeroepen, loop dan door de array van ready functies en roep elk aan in de volgorde waarin ze zijn toegevoegd. Zet een vlag om aan te geven dat deze allemaal zijn aangeroepen, zodat ze nooit meer dan één keer worden uitgevoerd.
  12. Maak de functie-array leeg, zodat eventuele closures die ze gebruiken vrijgemaakt kunnen worden.

Handlers geregistreerd met docReady() worden gegarandeerd uitgevoerd in de volgorde waarin ze zijn geregistreerd.

Als je docReady(fn) aanroept nadat het document al klaar is, zal de callback gepland worden om uit te voeren zodra de huidige thread van uitvoering is voltooid met setTimeout(fn, 1). Hierdoor kan de aanroepende code altijd aannemen dat het async callbacks zijn die later worden aangeroepen, zelfs als later is zodra de huidige thread van JS is afgelopen en het behoudt de volgorde van aanroepen.

Commentaren (16)

Uw methode (script plaatsen voor de afsluitende body tag)

<script>
   myFunction()
</script>

is een betrouwbare manier om oude en nieuwe browsers te ondersteunen.

Commentaren (2)

document.ondomcontentready=function(){} zou de truc moeten doen, maar het heeft niet'volledige browser compatibiliteit.

Het lijkt erop dat je gewoon jQuery min moet gebruiken

Commentaren (5)