Puro equivalente JavaScript di $.ready() di jQuery - come chiamare una funzione quando la pagina/DOM è pronta per essa

Ok, questa potrebbe essere solo una domanda stupida, anche se sono sicuro che ci sono molte altre persone che fanno la stessa domanda di tanto in tanto. Io, voglio solo essere sicuro al 100% in ogni caso. Con jQuery conosciamo tutti il meraviglioso

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

Tuttavia, diciamo che voglio eseguire una funzione che è scritta in JavaScript standard senza alcuna libreria di supporto, e che voglio lanciare una funzione non appena la pagina è pronta a gestirla. Qual è il modo corretto di avvicinarsi a questo?

So che posso fare:

window.onload="myFunction()";

...o posso usare il tag body:

<body onload="myFunction()">

...o posso anche provare in fondo alla pagina dopo tutto, ma alla fine body o html tag come:

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

Qual è un metodo cross-browser (vecchio/nuovo) compatibile con l'emissione di una o più funzioni in un modo come jQuery's $.ready()?

Soluzione

La cosa più semplice da fare in assenza di un framework che faccia tutta la compatibilità cross-browser per te è semplicemente mettere una chiamata al tuo codice alla fine del corpo. Questo è più veloce da eseguire di un gestore onload perché questo aspetta solo che il DOM sia pronto, non che tutte le immagini vengano caricate. E questo funziona in ogni browser.






Your HTML here

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

})();
</script>


Per i browser moderni (qualsiasi cosa da IE9 in poi e qualsiasi versione di Chrome, Firefox o Safari), se volete essere in grado di implementare un metodo jQuery come $(document).ready() che potete chiamare da qualsiasi luogo (senza preoccuparvi di dove sia posizionato lo script chiamante), potete semplicemente usare qualcosa come questo:

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

Uso:

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

Se hai bisogno di piena compatibilità cross-browser (incluse le vecchie versioni di IE) e non vuoi aspettare il window.onload, allora probabilmente dovresti andare a vedere come un framework come jQuery implementa il suo metodo $(document).ready(). È abbastanza complicato a seconda delle capacità del browser.

Per darvi una piccola idea di cosa fa jQuery (che funzionerà ovunque sia posizionato il tag script).

Se supportato, prova lo standard:

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

con un fallback a:

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

o per le vecchie versioni di IE, usa:

document.attachEvent("onreadystatechange", fn);

con un fallback a:

window.attachEvent("onload", fn);

E, ci sono alcuni work-around nel percorso del codice di IE che non seguo bene, ma sembra che abbia qualcosa a che fare con i frame.


Qui c'è un sostituto completo di jQuery .ready() scritto in semplice 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);

L'ultima versione del codice è condivisa pubblicamente su GitHub all'indirizzo https://github.com/jfriend00/docReady

Utilizzo:

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

Questo è stato testato in:

lingua: lang-none -->

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

Implementazione di lavoro e banco di prova: http://jsfiddle.net/jfriend00/YfD3C/


Ecco un riassunto di come funziona:

  1. Creiamo un IIFE (espressione di funzione immediatamente invocata) così possiamo avere variabili di stato non pubbliche.
  2. Dichiarare una funzione pubblica docReady(fn, context).
  3. Quando docReady(fn, context) viene chiamata, controlla se il gestore ready ha già sparato. Se è così, basta programmare il callback appena aggiunto per sparare subito dopo che questo thread di JS finisce con setTimeout(fn, 1).
  4. Se il gestore pronto non ha già sparato, allora aggiungi questo nuovo callback alla lista dei callback da chiamare in seguito.
  5. Controllare se il documento è già pronto. Se è così, esegui tutti i gestori pronti.
  6. Se non abbiamo ancora installato gli ascoltatori di eventi per sapere quando il documento è pronto, allora installateli ora.
  7. Se document.addEventListener esiste, allora installa i gestori di eventi usando .addEventListener() per entrambi gli eventi "DOMContentLoaded" e "load". L'evento "load" è un evento di riserva per sicurezza e non dovrebbe essere necessario.
  8. Se document.addEventListener non esiste, allora installa gestori di eventi usando .attachEvent() per gli eventi "onreadystatechange" e "onload".
  9. Nell'evento onreadystatechange, controlla se il document.readyState === "complete" e se è così, chiama una funzione per sparare tutti i gestori pronti.
  10. In tutti gli altri gestori di eventi, chiamate una funzione per attivare tutti i gestori pronti.
  11. Nella funzione per chiamare tutti i gestori pronti, controlla una variabile di stato per vedere se abbiamo già sparato. Se lo abbiamo fatto, non fare nulla. Se non siamo ancora stati chiamati, allora eseguite un ciclo attraverso l'array di funzioni pronte e chiamate ognuna di esse nell'ordine in cui sono state aggiunte. Impostate un flag per indicare che sono state chiamate tutte, in modo che non vengano mai eseguite più di una volta.
  12. Cancella l'array di funzioni in modo che qualsiasi chiusura che potrebbero usare possa essere liberata.

I gestori registrati con docReady() sono garantiti per essere lanciati nell'ordine in cui sono stati registrati.

Se si chiama docReady(fn) dopo che il documento è già pronto, il callback sarà programmato per essere eseguito non appena il thread di esecuzione corrente si concluderà usando setTimeout(fn, 1). Questo permette al codice chiamante di assumere sempre che siano callback asincroni che saranno chiamati più tardi, anche se più tardi è appena il thread corrente di JS finisce e conserva l'ordine di chiamata.

Commentari (16)

Il tuo metodo (mettere lo script prima del tag di chiusura del corpo)

<script>
   myFunction()
</script>

è un modo affidabile per supportare i vecchi e i nuovi browser.

Commentari (2)

document.ondomcontentready=function(){} dovrebbe fare il trucco, ma non ha piena compatibilità con i browser.

Sembra che dovresti semplicemente usare jQuery min

Commentari (5)