¿Scripts cargados asincrónicamente con controladores de eventos DOMContentLoaded o load no están siendo llamados?


Tengo un script con un controlador de eventos DOMContentLoaded -

document.addEventListener('DOMContentLoaded', function() {
    console.log('Hi');
});

Que estoy cargando asincrónicamente -

<script async src=script.js></script>

Sin embargo, el controlador de eventos nunca se llama. Si lo cargo sincrónicamente -

<script src=script.js></script>

Funciona bien.

(Incluso si cambio el evento DOMContentLoaded a un evento load, nunca se llama.)

¿Qué da? El controlador de eventos debe registrarse independientemente de cómo se cargue el script por el navegador, ¿no?

Editar: no funciona en Chrome 18.0.1025.11 beta pero, con DOMContentLoaded, hace en Firefox 11 beta (pero con load no lo hace). Imagínate.

¡OH GRANDES SEÑORES DE JAVASCRIPT Y DEL DOM, REZAD PARA MOSTRAR EL ERROR DE MIS CAMINOS!

Author: user1203233, 2012-02-11

4 answers

Al cargar el script de forma asíncrona, le estás diciendo al navegador que puede cargar ese script independientemente de las otras partes de la página. Eso significa que la página puede terminar de cargarse y puede dispararse DOMContentLoaded ANTES de que su script se cargue y antes de que se registre para el evento. Si eso sucede, te perderás el evento(ya ha sucedido cuando te registras).

En algunos navegadores, puede probar el documento para ver si ya está cargado. No he comprobado todo el navegador compatibilidad, pero en Firefox 3.6 +(MDN doc ), se puede comprobar:

if (document.readyState !== "loading")

Para ver si el documento ya está cargado. Si lo es, haz lo que quieras. Si no lo es, instala el oyente de eventos.

De hecho, como fuente de referencia e idea de implementación, jQuery hace esto mismo con su método .ready() y parece ampliamente soportado. jQuery tiene este código cuando se llama a .ready() que primero comprueba si el documento ya está cargado. Si es así, llama a la lista funciona inmediatamente en lugar de enlazar el receptor de eventos:

// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
    // Handle it asynchronously to allow scripts the opportunity to delay ready
    return setTimeout( jQuery.ready, 1 );
}
 52
Author: jfriend00,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-08 01:30:14

Esta no es la respuesta final pero me hizo entender por qué no es correcto usar async con un script que necesita modificar DOM, por lo que debe esperar al evento DOMContentLoaded. La esperanza podría ser beneficiosa.

introduzca la descripción de la imagen aquí

(Fuente: Ejecutando Su Código en el Momento Correcto desde kirupa.com )

 20
Author: Manuel Solorzano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-29 20:06:11

Una forma de evitar esto es usar el evento load en el objeto window.

Esto sucederá más tarde que DOMContentLoaded, pero al menos no tiene que preocuparse por perderse el evento.

window.addEventListener("load", function () {
   console.log('window loaded');
});

Si realmente necesita capturar el evento DOMContentLoaded, puede usar Promise object. Promise se resolverá incluso si sucedió antes:

HTMLDocument.prototype.ready = new Promise(function (resolve) {
if (document.readyState != "loading")
    return resolve();
else
    document.addEventListener("DOMContentLoaded", function () {
        return resolve();
    });
});

document.ready.then(function () {
    console.log("document ready");
});
 3
Author: user4617883,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-03 19:11:07

Simulando una conexión 3G lenta usando Chrome devtools Tuve problemas manipulando el DOM desde un script externo async, sin embargo este fragmento que encontré en algún lugar en GitHub lo resolvió para mí:

var DOMReady = function(callback) {document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);};
DOMReady(function() {
  //DOM is ready!
});

¿Puede alguien dar fe de esto?
¿Cómo se compara esto con las soluciones ya sugeridas?


También encontré una solución similar aquí:
https://javascript.info/onload-ondomcontentloaded#readystate
 1
Author: Malvoz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-06 22:00:16