1848 votes

L'équivalent en JavaScript pur de la fonction $.ready() de jQuery - comment appeler une fonction lorsque la page/le DOM est prêt(e) à l'accueillir.

Avec jQuery, nous connaissons tous la merveilleuse .ready() fonction :

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

Toutefois, disons que je veux exécuter une fonction écrite en JavaScript standard, sans bibliothèque de soutien, et que je veux lancer une fonction dès que la page est prête à la gérer. Quelle est la bonne façon d'aborder cette question ?

Je sais que je peux le faire :

window.onload="myFunction()";

Ou je peux utiliser le body étiquette :

<body onload="myFunction()">

Ou bien je peux même essayer en bas de la page après tout, mais la fin body o html tag comme :

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

Quelle est la méthode, compatible avec les navigateurs (anciens et nouveaux), qui permet d'envoyer une ou plusieurs fonctions à la manière de la fonction jQuery ? $.ready() ?

1 votes

La prise en charge des anciens navigateurs est une bonne chose, mais étant donné la vitesse à laquelle la technologie progresse et le fait que les gens semblent rattraper leur retard plus rapidement de nos jours, elle n'est pas nécessaire à 100 %, mais c'est un bon bonus si possible. Dans l'ensemble, j'essaie de comprendre si l'un de ces éléments est la norme dans les navigateurs. Est-ce qu'ils fonctionnent tous ? Le choix du navigateur est-il important ? S'ils fonctionnent tous, quel serait le meilleur choix par rapport aux autres ?

11 votes

0 votes

@clexmond mettre script en bas vs en haut est quelque chose que j'ai toujours compris pour diverses raisons. J'ai tendance à le placer en bas, dans des fichiers externes ou sur la page elle-même. Mais ces deux dernières années, j'ai été gâté par jQuery et d'autres libraires. Cependant, je suis maintenant dans une sorte de croisade pour essayer de mieux comprendre le javascript par lui-même, sans libraire. Donc, malgré ce que je sais de javascript, cette simple petite chose me déstabilise un peu car je veux m'assurer que tout script que je fais suit une méthodologie compatible avec tous les navigateurs.

2580voto

jfriend00 Points 152127

En l'absence d'un framework qui assure la compatibilité entre navigateurs, le plus simple est de placer un appel à votre code à la fin du corps du texte. Cette méthode est plus rapide à exécuter qu'un onload car il attend seulement que le DOM soit prêt, et non que toutes les images soient chargées. Et cela fonctionne dans tous les navigateurs.

<!doctype html>
<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>

Pour les navigateurs modernes (à partir d'IE9 et plus récents et toute version de Chrome, Firefox ou Safari), si vous voulez être en mesure de mettre en œuvre une fonction jQuery comme $(document).ready() que vous pouvez appeler de n'importe où (sans vous soucier de l'emplacement du script appelant), vous pouvez simplement utiliser quelque chose comme ceci :

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

Utilisation :

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

Si vous avez besoin d'une compatibilité totale entre les différents navigateurs (y compris les anciennes versions d'IE) et que vous ne voulez pas attendre la mise en place de l window.onload alors vous devriez probablement aller voir comment un framework comme jQuery implémente son $(document).ready() méthode. Elle est assez complexe et dépend des capacités du navigateur.

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

S'il est supporté, il essaie la norme :

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

avec une solution de repli :

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

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

document.attachEvent("onreadystatechange", fn);

avec une solution de repli :

window.attachEvent("onload", fn);

Et il existe des solutions de contournement dans le chemin de code d'IE que je ne suis pas tout à fait, mais il semble que cela ait quelque chose à voir avec les cadres.


Voici un substitut complet de la méthode jQuery .ready() é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 suivante https://github.com/jfriend00/docReady

Utilisation :

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

Ceci 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 œuvre et banc d'essai : http://jsfiddle.net/jfriend00/YfD3C/


Voici un résumé de son fonctionnement :

  1. Créer un IIFE (expression de fonction immédiatement invoquée) afin que nous puissions avoir des variables d'état non publiques.
  2. Déclarer une fonction publique docReady(fn, context)
  3. Cuando docReady(fn, context) est appelé, vérifiez si le gestionnaire ready a déjà été activé. Si c'est le cas, il suffit de programmer le nouveau callback pour qu'il se déclenche juste après que ce fil de JS se termine avec setTimeout(fn, 1) .
  4. Si le gestionnaire ready n'a pas encore été activé, ajoutez ce nouveau rappel à la liste des rappels à appeler ultérieurement.
  5. Vérifiez si le document est déjà prêt. Si oui, exécutez tous les gestionnaires de documents prêts.
  6. Si nous n'avons pas encore installé de récepteurs d'événements pour savoir quand le document est prêt, installez-les maintenant.
  7. Si document.addEventListener existe, puis installez les gestionnaires d'événements en utilisant .addEventListener() pour les deux "DOMContentLoaded" y "load" événements. La "charge" est un événement de secours pour la sécurité et ne devrait pas être nécessaire.
  8. Si document.addEventListener n'existe pas, alors installez des gestionnaires d'événements en utilisant .attachEvent() pour "onreadystatechange" y "onload" événements.
  9. Dans le onreadystatechange vérifiez si l'événement document.readyState === "complete" et si c'est le cas, appeler 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 qui appelle tous les gestionnaires prêts, vérifiez une variable d'état pour voir si nous avons déjà tiré. Si c'est le cas, ne faites rien. Si nous n'avons pas encore été appelés, alors bouclez à travers le tableau des fonctions prêtes et appelez chacune d'entre elles dans l'ordre où elles ont été ajoutées. Définissez un drapeau pour indiquer qu'elles ont toutes été appelées afin qu'elles ne soient jamais exécutées plus d'une fois.
  12. Effacer le tableau des fonctions pour que toutes les fermetures qu'elles utilisent puissent être libérées.

Manipulateurs enregistrés auprès de docReady() sont garantis d'être tirés dans l'ordre où ils ont été enregistrés.

Si vous appelez docReady(fn) alors que le document est déjà prêt, la fonction de rappel sera programmée pour être exécutée dès que le fil d'exécution actuel sera terminé en utilisant la fonction setTimeout(fn, 1) . Cela permet au code appelant de toujours supposer qu'il s'agit de callbacks asynchrones qui seront appelés plus tard, même si plus tard est dès que le thread actuel de JS se termine et cela préserve l'ordre d'appel.

0 votes

Je n'ai pas pensé à celle-là. Les anciens navigateurs supporteront-ils également cette fonctionnalité ? Ou s'agit-il plutôt d'une approche moderne ?

3 votes

Placer le code d'initialisation de la page après le corps du texte fonctionne partout, même dans les très anciens navigateurs. Grâce à la possibilité d'utiliser document.write() dans votre javascript et la simplicité qu'il entraîne dans le codage/la mise en œuvre, tout le javascript qui n'est pas explicitement marqué comme defer ou async est chargé séquentiellement lorsqu'il est rencontré dans la page et tout ce qui est avant lui dans le fichier a déjà été exécuté ou analysé.

1 votes

Ajout d'une implémentation complète d'une fonction javascript simple appelée docReady() .

342voto

Tom Stickel Points 4201

Si vous faites VANILLA simple JavaScript sans jQuery, alors vous devez utiliser (Internet Explorer 9 ou plus) :

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

Ci-dessus, l'équivalent de jQuery .ready :

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

Ce qui pourrait AUSSI être écrit SHORTHAND comme ceci, que jQuery exécutera après le ready même se produit .

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

À NE PAS ASSOCIER AVEC CE QUI SUIT (qui n'est pas censé être DOM ready) :

NE PAS utiliser un IIFE comme ça, qui est auto-exécutoire :

 Example:

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

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

189voto

Ram swaroop Points 309

Je voudrais mentionner ici quelques-unes des voies possibles ainsi qu'un une astuce purement 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 original c'est que nous vérifions le document.readyState propriété. Si elle contient la chaîne de caractères in (dans le mot anglais uninitialized y loading les deux premiers États prêts pour le DOM sur 5), nous fixons un délai d'attente et vérifions à nouveau. Sinon, nous exécutons la fonction passée.

Et voici le jsFiddle pour le tour qui fonctionne sur tous les navigateurs.

Merci à Tutorialzine pour avoir inclus ceci dans leur livre.

81voto

PhilT Points 863

Testé dans IE9, et les dernières versions de Firefox et Chrome et également supporté dans IE8.

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

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

MISE À JOUR - version réutilisable

Je viens de développer ce qui suit. C'est un équivalent assez simpliste de jQuery ou de Dom ready sans rétrocompatibilité. Il a probablement besoin d'être affiné. Testé dans les dernières versions de Chrome, Firefox et IE (10/11) et devrait fonctionner dans les navigateurs plus anciens comme commenté. 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;

Utilisation :

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

Il est écrit pour gérer le chargement asynchrone de JS mais vous pourriez vouloir charger de manière synchrone ce script d'abord, à moins que vous ne le réduisiez. Je l'ai trouvé utile dans le développement.

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

1 votes

Cela fonctionne également dans IE8. Il ne fonctionne pas dans les versions inférieures ou dans tout IE en mode de compatibilité. Merci.

1 votes

Voulez-vous dire addEventListener @rogerdpack ? attachEvent n'est plus supporté par aucun navigateur mais je comprends votre point de vue. Je vais mettre à jour.

24voto

Lorcan O'Neill Points 508

Les bonnes gens de HubSpot ont une ressource où vous pouvez trouver des méthodologies purement Javascript pour réaliser beaucoup de choses avec 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'); });

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X