2012-02-11 33 views
22

Tengo un guión con un evento DOMContentLoaded handler-¿Scripts cargados con Async con DOMContentLoaded o load event handlers que no se llaman?

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

Qué estoy cargando asynchronously-

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

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

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

Funciona bien.

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

Lo que da? El controlador de eventos debe registrarse independientemente de cómo el navegador cargue el script, ¿no?

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

OH GRANDES SEÑORES DE JAVASCRIPT Y EL DOM, OREN MOSTRAR EL ERROR DE MIS MANERAS!

+1

Al cargar el guión asynchrounously, le está diciendo al navegador que puede cargar ese guión independientemente de las otras partes de la página. Eso significa que la página puede terminar de cargarse y puede desencadenar DOMContentLoaded ANTES de que se cargue el script y antes de que se registre para el evento. Si eso sucede, te perderás el evento (ya ha sucedido cuando te registras). – jfriend00

+0

Oh, interedasting. Así que realmente debería probar para el evento que ya se haya disparado - (** edit **: en realidad, dado que confío en que el evento se active * después * del script, no debería ser asíncrono en absoluto). Guay. Escríbalo como respuesta y puedes tener un voto positivo :) – user1203233

Respuesta

46

Al cargar la secuencia de comandos de forma asíncrona, le está diciendo al navegador que puede cargar esa secuencia de comandos independientemente de las otras partes de la página. Eso significa que la página puede terminar de cargarse y puede disparar DOMContentLoaded ANTES de que se cargue la secuencia de comandos 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 toda la compatibilidad del navegador, pero en Firefox 3.6+ (MDN doc), se puede comprobar:

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

para ver si el documento ya está cargado. Si es así, solo haga su trabajo. Si no es así, instale su detector de eventos.

De hecho, como fuente de referencia e idea de implementación, jQuery hace lo mismo con su método .ready() y parece ampliamente compatible. jQuery tiene este código cuando se llama al .ready() que primero verifica si el documento ya está cargado. Si es así, se llama a la función listo inmediatamente en vez de unir el detector 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); 
} 
+0

Sí señor, eso es todo lo que necesitaba saber. Mentí sobre el voto favorable, no tengo suficientes representantes;) Espero que alguien tenga la amabilidad de votar por esto :) – user1203233

+0

En caso de que 'document.readyState' sea Gecko-only: el camino que está funcionando es otro Controlador de eventos 'DOMContentLoaded' * sincrónicamente * que establecerá un indicador global cuando se ejecute, con lo cual podremos verificar después :) ** Editar **: Oh, esperen todos los navegadores geniales que lo soporten, así que bien – user1203233

+7

Probando cosas, resulta comprobar si 'document.readyState == 'complete'' es * no suficiente * - cuando se estaba realizando la comprobación,' document.readyState' era 'interactive'. La comprobación de 'document.readyState! = 'Loading'' funciona bien :) – user1203233

17

Ésta no es la respuesta final, pero me hizo entender por qué no es correcto el uso asíncrono con un guión que resulta necesario modificar DOM, por lo que debe espera al evento DOMContentLoaded. La esperanza podría ser beneficiosa.

enter image description here

(Fuente: Running Your Code at the Right Time from kirupa.com)

2

Una forma de evitar esto es usar el evento de carga en el objeto ventana.

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

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

Si realmente necesita ver el evento DOMContentLoaded puede usar el objeto Promesa. Promesa se resolverá incluso si hubiera sucedido 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"); 
}); 
Cuestiones relacionadas