web-dev-qa-db-fra.com

Équivalent JavaScript pur de $ .ready () de jQuery - comment appeler une fonction lorsque la page / DOM est prête pour celle-ci

D'accord, c'est peut-être une question idiote, même si je suis sûr qu'il y a beaucoup d'autres personnes qui posent la même question de temps en temps. Moi, je veux juste en être sûr à 100%. Avec jQuery, nous connaissons tous le merveilleux

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

Cependant, supposons que je souhaite exécuter une fonction écrite en JavaScript standard sans aucune bibliothèque le sauvegardant, et que je souhaite lancer une fonction dès que la page est prête à la gérer. Quelle est la bonne façon d'aborder cela?

Je sais que je peux faire:

window.onload="myFunction()";

... ou je peux utiliser la balise body:

<body onload="myFunction()">

... ou je peux même essayer au bas de la page après tout, mais la balise end body ou html ressemble à ceci:

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

Qu'est-ce qu'une méthode compatible avec plusieurs navigateurs (ancien/nouveau) permettant d'émettre une ou plusieurs fonctions à la manière de jQuery $.ready()?

1177
chris

La chose la plus simple à faire en l’absence d’un cadre prenant en charge toute la compatibilité entre navigateurs consiste simplement à appeler votre code à la fin du corps. Cela est plus rapide à exécuter qu'un gestionnaire onload car il attend seulement que le DOM soit prêt, pas pour que toutes les images soient chargées. Et cela fonctionne dans tous les navigateurs.

<html>
<head>
</head>
<body>
Your HTML here

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

})();
</script>
</body>
</html>

Si vous ne voulez vraiment pas le faire de cette façon et que vous avez besoin d'une compatibilité entre navigateurs et que vous ne voulez pas attendre window.onload, alors vous devriez probablement aller voir comment un framework comme jQuery implémente sa $(document).ready() méthode. C'est assez impliqué en fonction des capacités du navigateur.

Pour vous donner une petite idée de ce que fait jQuery (ce qui fonctionnera partout où la balise script est placée).

Si supporté, il essaye le standard:

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

avec un repli sur:

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

ou pour les anciennes versions d'IE, il utilise:

document.attachEvent("onreadystatechange", fn);

avec un repli sur:

window.attachEvent("onload", fn);

Et, il y a quelques solutions de contournement dans le chemin de code IE que je ne suis pas très bien, mais il semble que cela ait quelque chose à voir avec les cadres.


Voici un substitut complet pour .ready() de jQuery écrit en 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);

La dernière version du code est partagée publiquement sur GitHub à l’adresse https://github.com/jfriend00/docReady

Usage:

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

Cela a été testé dans:

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

Mise en oeuvre et banc d’essai: http://jsfiddle.net/jfriend00/YfD3C/


Voici un résumé de la façon dont cela fonctionne:

  1. Créez un IIFE (expression de fonction immédiatement appelée) afin que nous puissions avoir des variables d'état non publiques.
  2. Déclarer une fonction publique docReady(fn, context)
  3. Lorsque docReady(fn, context) est appelé, vérifiez si le gestionnaire Prêt est déjà activé. Si c'est le cas, programmez simplement le rappel nouvellement ajouté pour qu'il soit déclenché juste après que ce thread de JS se termine avec setTimeout(fn, 1).
  4. Si le gestionnaire prêt n'a pas déjà été déclenché, ajoutez ce nouveau rappel à la liste des rappels à appeler ultérieurement.
  5. Vérifiez si le document est déjà prêt. Si c'est le cas, exécutez tous les gestionnaires prêts.
  6. Si nous n'avons pas encore installé les écouteurs d'événements pour savoir quand le document est prêt, installez-les maintenant.
  7. Si document.addEventListener existe, installez des gestionnaires d'événements en utilisant .addEventListener() pour les deux événements "DOMContentLoaded" et "load". Le "chargement" est un événement de sauvegarde pour la sécurité et ne devrait pas être nécessaire.
  8. Si document.addEventListener n'existe pas, installez des gestionnaires d'événements en utilisant .attachEvent() pour "onreadystatechange" et "onload".
  9. Dans l'événement onreadystatechange, vérifiez si le document.readyState === "complete" et, le cas échéant, appelez une fonction pour déclencher tous les gestionnaires prêts.
  10. Dans tous les autres gestionnaires d'événements, appelez une fonction pour déclencher tous les gestionnaires prêts.
  11. Dans la fonction pour appeler tous les gestionnaires prêts, vérifiez une variable d'état pour voir si nous avons déjà déclenché. Si nous avons, ne fais rien. Si nous n'avons pas encore été appelés, passez en revue le tableau des fonctions ready et appelez chacune d'elles dans l'ordre dans lequel elles ont été ajoutées. Définissez un drapeau pour indiquer que tous ont été appelés pour qu’ils ne soient jamais exécutés plus d’une fois.
  12. Effacez le tableau de fonctions pour libérer les fermetures éventuellement utilisées.

Les gestionnaires enregistrés avec docReady() ont la garantie d'être licenciés dans l'ordre dans lequel ils ont été enregistrés.

Si vous appelez docReady(fn) après que le document est déjà prêt, l'exécution du rappel est planifiée dès que le thread d'exécution en cours est terminé avec setTimeout(fn, 1). Cela permet au code d'appel de toujours supposer qu'il s'agit de rappels asynchrones qui seront appelés ultérieurement, même si plus tard le sera dès que le thread actuel de JS se terminera et qu'il préservera l'ordre d'appel.

1693
jfriend00

Je voudrais mentionner certaines des manières possibles ici avec une astuce pure en javascript qui fonctionne sur tous les navigateurs :

// with jQuery 
$(document).ready(function(){ /* ... */ });

// shorter jQuery version 
$(function(){ /* ... */ });

// without jQuery (doesn't work in older IEs)
document.addEventListener('DOMContentLoaded', function(){ 
    // your code goes here
}, false);

// and here's the trick (works everywhere)
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
// use like
r(function(){
    alert('DOM Ready!');
});

L'astuce ici, comme l'explique le auteur d'origine , est que nous vérifions la propriété document.readyState . S'il contient la chaîne in (comme dans uninitialized et loading, les deux premiers états de disponibilité DOM sur 5), nous fixons un délai d'expiration et vérifions à nouveau. Sinon, nous exécutons la fonction passée.

Et voici le jsFiddle pour l'astuce qui fonctionne sur tous les navigateurs.

Merci à Tutorialzine d’avoir inclus cela dans leur livre.

142
Ram Patra

Si vous faites Vanilla simple JavaScript sans jQuery, vous devez utiliser (Internet Explorer 9 ou version ultérieure):

document.addEventListener("DOMContentLoaded", function(event) {
    // Your code to run since DOM is loaded and ready
});

Ci-dessus est l’équivalent de jQuery .ready:

$(document).ready(function() {
    console.log("Ready!");
});

Ce qui pourrait aussi être écrit SHORTHAND comme ceci, qui jQuery sera exécuté après le prêt même se produit .

$(function() {
    console.log("ready!");
});

Ne pas confondre avec BELOW (qui n'est pas censé être prêt pour le DOM):

NE PAS utiliser un IIFE comme ceci qui s'exécute automatiquement:

 Example:

(function() {
   // Your page initialization code here  - WRONG
   // The DOM will be available here   - WRONG
})();

Cet IIFE n'attendra PAS le chargement de votre DOM. (Je parle même de la dernière version du navigateur Chrome!)

123
Tom Stickel

Testé dans IE9 et les derniers Firefox et Chrome et également pris en charge dans IE8.

document.onreadystatechange = function () {
  var state = document.readyState;
  if (state == 'interactive') {
      init();
  } else if (state == 'complete') {
      initOnCompleteLoad();
  }
}​;

Exemple: http://jsfiddle.net/electricvisions/Jacck/

UPDATE - version réutilisable

Je viens de développer ce qui suit. C'est un équivalent assez simpliste de jQuery ou Dom ready sans compatibilité avec les versions antérieures. Cela a probablement besoin d'être peaufiné. Testé dans les dernières versions de Chrome, Firefox et IE (10/11) et devrait fonctionner dans les navigateurs plus anciens comme indiqué. Je mettrai à jour si je trouve des problèmes.

window.readyHandlers = [];
window.ready = function ready(handler) {
  window.readyHandlers.Push(handler);
  handleState();
};

window.handleState = function handleState () {
  if (['interactive', 'complete'].indexOf(document.readyState) > -1) {
    while(window.readyHandlers.length > 0) {
      (window.readyHandlers.shift())();
    }
  }
};

document.onreadystatechange = window.handleState;

Usage:

ready(function () {
  // your code here
});

Il est écrit pour gérer le chargement asynchrone de JS, mais vous voudrez peut-être tout d'abord synchroniser ce script, sauf si vous réduisez la taille. Je l'ai trouvé utile en développement.

Les navigateurs modernes prennent également en charge le chargement asynchrone des scripts, ce qui améliore encore l'expérience. La prise en charge async signifie que plusieurs scripts peuvent être téléchargés simultanément tout en restituant la page. Faites simplement attention lorsque vous dépendez d'autres scripts chargés de manière asynchrone ou utilisez un minifier ou quelque chose comme browserify pour gérer les dépendances.

77
PhilT

Les bons employés de HubSpot ont une ressource où vous pouvez trouver des méthodologies Javascript pures pour obtenir beaucoup de qualité jQuery, y compris ready

http://youmightnotneedjquery.com/#ready

function ready(fn) {
  if (document.readyState != 'loading'){
    fn();
  } else if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', fn);
  } else {
    document.attachEvent('onreadystatechange', function() {
      if (document.readyState != 'loading')
        fn();
    });
  }
}

exemple d'utilisation en ligne:

ready(function() { alert('hello'); });
19
Lorcan O'Neill

Votre méthode (placer le script avant la balise body de fermeture)

<script>
   myFunction()
</script>
</body>
</html>

est un moyen fiable de prendre en charge les anciens et les nouveaux navigateurs.

7
Kernel James

Je ne sais pas trop ce que vous demandez, mais peut-être que cela peut aider:

window.onload = function(){
    // Code. . .

}

Ou:

window.onload = main;

function main(){
    // Code. . .

}
5
Zak The Hat

Prêt

function ready(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();}

Utiliser comme

ready(function(){
    //some code
});

Pour un code invoquant

(function(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();})(function(){

    //Some Code here
    //DOM is avaliable
    //var h1s = document.querySelector("h1");

});

Soutien: IE9 +

4
Vitim.us

Voici une version nettoyée et non utilisable par eval de la variété Ram-swaroop's "fonctionne dans tous les navigateurs" - fonctionne dans tous les navigateurs!

function onReady(yourMethod) {
  var readyStateCheckInterval = setInterval(function() {
    if (document && document.readyState === 'complete') { // Or 'interactive'
      clearInterval(readyStateCheckInterval);
      yourMethod();
    }
  }, 10);
}
// use like
onReady(function() { alert('hello'); } );

Cependant, il attend 10 ms supplémentaires pour fonctionner. Voici donc un moyen plus compliqué qui ne devrait pas:

function onReady(yourMethod) {
  if (document.readyState === 'complete') { // Or also compare to 'interactive'
    setTimeout(yourMethod, 1); // Schedule to run immediately
  }
  else {
    readyStateCheckInterval = setInterval(function() {
      if (document.readyState === 'complete') { // Or also compare to 'interactive'
        clearInterval(readyStateCheckInterval);
        yourMethod();
      }
    }, 10);
  }
}

// Use like
onReady(function() { alert('hello'); } );

// Or
onReady(functionName);

Voir aussi Comment vérifier si DOM est prêt sans framework?.

3
rogerdpack

document.ondomcontentready=function(){} devrait faire l'affaire, mais il n'est pas complètement compatible avec le navigateur.

On dirait que vous devriez juste utiliser jQuery min

1
maxhud