Vanilla JavaScript equivalente a $.ready() de jQuery: cómo llamar a una función cuando la página/DOM está lista para ello [duplicate]

11 minutos de lectura

avatar de usuario
cris

Con jQuery, todos sabemos lo maravilloso .ready() función:

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

Sin embargo, digamos que quiero ejecutar una función que está escrita en JavaScript estándar sin una biblioteca que la respalde, y que quiero iniciar una función tan pronto como la página esté lista para manejarla. ¿Cuál es la forma correcta de abordar esto?

Sé que puedo hacer:

window.onload="myFunction()";

O puedo usar el body etiqueta:

<body onload="myFunction()">

O incluso puedo probar en la parte inferior de la página después de todo, pero al final body o html etiqueta como:

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

¿Qué es un método compatible con varios navegadores (antiguo/nuevo) para emitir una o más funciones de manera similar a jQuery? $.ready()?

  • Vea esto: stackoverflow.com/questions/799981/…

    – dan a.

    28 de marzo de 2012 a las 0:00

avatar de usuario
amigo00

Lo más simple que puede hacer en ausencia de un marco que haga toda la compatibilidad entre navegadores por usted es simplemente llamar a su código al final del cuerpo. Esto es más rápido de ejecutar que un onload controlador porque esto espera solo a que el DOM esté listo, no a que se carguen todas las imágenes. Y esto funciona en todos los navegadores.

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

Para navegadores modernos (cualquiera desde IE9 y posteriores y cualquier versión de Chrome, Firefox o Safari), si desea poder implementar un jQuery como $(document).ready() método al que puede llamar desde cualquier lugar (sin preocuparse de dónde se coloca el script de llamada), puede usar algo como esto:

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

Si necesita compatibilidad total entre navegadores (incluidas las versiones antiguas de IE) y no quiere esperar a que window.onloadentonces probablemente debería ver cómo un marco como jQuery implementa su $(document).ready() método. Es bastante complicado dependiendo de las capacidades del navegador.

Para darle una pequeña idea de lo que hace jQuery (que funcionará donde sea que se coloque la etiqueta del script).

Si es compatible, prueba el estándar:

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

con un respaldo a:

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

o para versiones anteriores de IE, utiliza:

document.attachEvent("onreadystatechange", fn);

con un respaldo a:

window.attachEvent("onload", fn);

Y hay algunas soluciones alternativas en la ruta del código de IE que no sigo del todo, pero parece que tiene algo que ver con los marcos.


Aquí hay un sustituto completo de jQuery .ready() escrito en javascript simple:

(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 última versión del código se comparte públicamente en GitHub en https://github.com/jfriend00/docReady

Uso:

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

Esto ha sido probado en:

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

Implementación de trabajo y banco de pruebas: http://jsfiddle.net/jfriend00/YfD3C/


Aquí hay un resumen de cómo funciona:

  1. Crear un IIFE (expresión de función invocada inmediatamente) para que podamos tener variables de estado no públicas.
  2. Declarar una función pública docReady(fn, context)
  3. Cuando docReady(fn, context) se llama, verifique si el controlador listo ya se ha disparado. Si es así, simplemente programe la devolución de llamada recién agregada para que se active justo después de que este hilo de JS finalice con setTimeout(fn, 1).
  4. Si el controlador listo aún no se ha activado, agregue esta nueva devolución de llamada a la lista de devoluciones de llamada que se llamarán más adelante.
  5. Compruebe si el documento ya está listo. Si es así, ejecute todos los controladores preparados.
  6. Si aún no hemos instalado detectores de eventos para saber cuándo estará listo el documento, instálelos ahora.
  7. Si document.addEventListener existe, luego instale los controladores de eventos usando .addEventListener() para ambos "DOMContentLoaded" y "load" eventos. La “carga” es un evento de respaldo por seguridad y no debería ser necesario.
  8. Si document.addEventListener no existe, luego instale los controladores de eventos usando .attachEvent() por "onreadystatechange" y "onload" eventos.
  9. En el onreadystatechange evento, verifique si el document.readyState === "complete" y si es así, llame a una función para activar todos los controladores listos.
  10. En todos los demás controladores de eventos, llame a una función para activar todos los controladores listos.
  11. En la función para llamar a todos los controladores listos, verifique una variable de estado para ver si ya hemos disparado. Si tenemos, no hagamos nada. Si aún no hemos sido llamados, recorra la matriz de funciones listas y llame a cada una en el orden en que se agregaron. Establezca una bandera para indicar que todos estos han sido llamados para que nunca se ejecuten más de una vez.
  12. Borre la matriz de funciones para que se puedan liberar los cierres que puedan estar usando.

Manipuladores registrados en docReady() están garantizados para ser despedidos en el orden en que fueron registrados.

si llamas docReady(fn) una vez que el documento ya esté listo, la devolución de llamada se programará para ejecutarse tan pronto como se complete el hilo de ejecución actual usando setTimeout(fn, 1). Esto permite que el código de llamada asuma siempre que son devoluciones de llamada asíncronas que se llamarán más tarde, incluso si más tarde es tan pronto como finaliza el hilo actual de JS y conserva el orden de llamada.

  • ¿Por qué se usa setTimeout(fn, 1) en lugar de setTimeout(fn, 0)?

    – David largo

    9 de febrero a las 2:11

  • @David: en realidad no importa, ya que el navegador tiene un tiempo de espera mínimo de ~ 4 ms de todos modos. La idea general es que queremos comunicar al lector del código que este setTimeout() se activará en un tick futuro del bucle de eventos, no inmediatamente. Mientras que incluso setTimeout(fn, 0) se disparará en un tic futuro del ciclo de eventos, pensé que sería más claro para un lector menos educado del código si usaba un valor distinto de cero para el tiempo para ilustrar que sucederá en el futuro, no inmediatamente. No es un gran problema de cualquier manera.

    – jfriend00

    9 de febrero a las 2:41

avatar de usuario
tom stickel

si estas haciendo VAINILLA sencillo JavaScript sin jQuery, entonces debe usar (Internet Explorer 9 o posterior):

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

Arriba está el equivalente de jQuery .ready:

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

Que TAMBIÉN podría escribirse en forma abreviada de esta manera, que jQuery ejecutará después de que ocurra el evento listo.

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

NO CONFUNDIRSE con ABAJO (que no está destinado a estar listo para DOM):

NO use un IIFE como este que es autoejecutable:

 Example:

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

Este IIFE NO esperará a que se cargue tu DOM. (¡Incluso estoy hablando de la última versión del navegador Chrome!)

  • play() falló porque el usuario no interactuó con el documento primero

    – CS QGB

    24 de noviembre de 2021 a las 10:16

  • sí, el primero para mí: D si desea agregar un script puro al editor de scripts de Sharepoint, use esto … document.addEventListener (“DOMContentLoaded”, función (evento)

    – señorita CEREN

    16 de febrero a las 22:14

avatar de usuario
carnero patra

Me gustaría mencionar algunas de las formas posibles aquí junto con una truco de javascript puro que funciona en todos los navegadores:

// 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!');
});

El truco aquí, como lo explica el autor originales que estamos comprobando el documento.readyState propiedad. Si contiene la cadena in (como en uninitialized y loadinglos primeros dos Estados listos para DOM de 5) establecemos un tiempo de espera y comprobamos de nuevo. De lo contrario, ejecutamos la función pasada.

Y aquí está el jsviolín por el truco que funciona en todos los navegadores.

Gracias a Tutorialzine por incluir esto en su libro.

  • Muy mal enfoque, usando un bucle de tiempo de espera con un intervalo arbitrario de 9 ms y usando eval. Además, verificar solo /in/ no tiene mucho sentido.

    – Vitim.es

    22 de noviembre de 2015 a las 14:51

Probado en IE9 y los últimos Firefox y Chrome y también compatible con IE8.

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

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

ACTUALIZACIÓN – versión reutilizable

Acabo de desarrollar lo siguiente. Es un equivalente bastante simplista de jQuery o Dom ready sin compatibilidad con versiones anteriores. Probablemente necesite más refinamiento. Probado en las últimas versiones de Chrome, Firefox e IE (10/11) y debería funcionar en navegadores más antiguos como se comentó. Actualizaré si encuentro algún problema.

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;

Uso:

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

Está escrito para manejar la carga asíncrona de JS, pero es posible que desee sincronizar la carga de este script primero a menos que esté minimizando. Lo he encontrado útil en el desarrollo.

Los navegadores modernos también admiten la carga asíncrona de scripts, lo que mejora aún más la experiencia. El soporte para asíncrono significa que se pueden descargar múltiples scripts simultáneamente mientras se sigue representando la página. Solo tenga cuidado cuando dependa de otros scripts cargados de forma asincrónica o use un minificador o algo como browserify para manejar las dependencias.

avatar de usuario
Lorcan O’Neill

La buena gente de HubSpot tiene un recurso donde puede encontrar metodologías Javascript puras para lograr muchas bondades de jQuery, que incluyen ready

http://es posible que no necesite jquery.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();
    });
  }
}

ejemplo de uso en línea:

ready(function() { alert('hello'); });

avatar de usuario
Pedro Mortensen

No estoy muy seguro de lo que estás preguntando, pero tal vez esto pueda ayudar:

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

}

O:

window.onload = main;

function main(){
    // Code. . .

}

avatar de usuario
rogerdpack

Su método (colocar el script antes de la etiqueta del cuerpo de cierre)

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

es una forma confiable de admitir navegadores antiguos y nuevos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad