2012-02-11 12 views
22

Io ho uno script con un evento DOMContentLoaded handler-Gli script caricati con Async con DOMContentLoaded o carichi i gestori di eventi non vengono chiamati?

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

quale sto caricando asynchronously-

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

Tuttavia, il gestore di eventi non viene mai chiamato. Se lo carico in modo sincrono -

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

Funziona bene.

(Anche se cambio l'evento DOMContentLoaded ad un evento load, non è mai chiamato.)

Che cosa dà? Il gestore di eventi dovrebbe essere registrato indipendentemente dal modo in cui lo script è caricato dal browser, no?

Edit: Non funziona su Chrome 18.0.1025.11 beta ma, con DOMContentLoaded, è fa su Firefox 11 beta (ma con load non è così). Vai a capire.

OH I GRANDI SIGNORI DI JAVASCRIPT E IL DOM, PREGHI MOSTRA L'ERRORE DEI MIEI MODI!

+1

Caricando lo script in modo asincrono, si sta dicendo al browser che si può caricare lo script indipendentemente dalle altre parti della pagina. Ciò significa che la pagina potrebbe terminare il caricamento e potrebbe generare DOMContentLoaded PRIMA che lo script venga caricato e prima che si registri per l'evento. Se ciò accade, perderai l'evento (è già successo quando ti registri). – jfriend00

+0

Oh interingua. Quindi davvero dovrei provare per l'evento che ha già sparato - (** edit **: in realtà, dal momento che faccio affidamento sull'evento che si attiva * dopo * lo script, non dovrebbe essere affatto asincrono). Freddo. Scrivilo come risposta e puoi avere un upvote :) – user1203233

risposta

46

Caricando lo script in modo asincrono, stai dicendo al browser che può caricare lo script indipendentemente dalle altre parti della pagina. Ciò significa che la pagina potrebbe terminare il caricamento e potrebbe sparare DOMContentLoaded PRIMA che lo script sia caricato e prima che si registri per l'evento. Se ciò accade, perderai l'evento (è già successo quando ti registri).

In alcuni browser, è possibile verificare il documento per vedere se è già caricato. Non ho controllato tutta la compatibilità del browser, ma in Firefox 3.6+ (MDN doc), è possibile controllare:

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

per vedere se il documento è già caricato. Se lo è, fai i tuoi affari. In caso contrario, installa il listener di eventi.

In effetti, come origine di riferimento e idea di implementazione, jQuery fa esattamente la stessa cosa con il suo metodo .ready() e sembra ampiamente supportato. jQuery ha questo codice quando viene chiamato .ready() che controlla prima se il documento è già stato caricato. Se è così, si chiama la funzione pronta subito, piuttosto che legare il listener di eventi:

// 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ì signore, questo è tutto ciò che dovevo sapere. Ho mentito riguardo l'upvote-ho avuto abbastanza rep;) Spero che qualcuno sia così gentile da votare per te anche questo :) :) – user1203233

+0

Nel caso in cui 'document.readyState' è solo Gecko: il modo in cui sta girando un'altra Gestore di eventi 'DOMContentLoaded' * in modo sincrono * che imposta un flag globale durante l'esecuzione, che possiamo controllare successivamente :) ** Modifica **: Oh aspetta che tutti i browser lo supportino quindi andiamo bene – user1203233

+7

Provando, si scopre il controllo di 'document.readyState == 'complete'' è * non sufficiente * - quando il controllo stava avvenendo,' document.readyState' era 'interactive'. Il controllo di 'document.readyState! = 'Loading'' funziona bene però :) – user1203233

17

Questa non è la risposta finale, ma mi ha fatto capire perché non è corretto utilizzando asincrona con uno script che hanno bisogno di modificare DOM, così deve attendi all'evento DOMContentLoaded. La speranza potrebbe essere utile.

enter image description here

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

2

Un modo per aggirare questo è quello di utilizzare l'evento carico sul oggetto window.

Questo accadrà più tardi di DOMContentLoaded, ma almeno non devi preoccuparti di perdere l'evento.

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

Se è davvero necessario catturare l'evento DOMContentLoaded, è possibile utilizzare l'oggetto Promise. Promessa otterrà risolto, anche se è successo in precedenza:

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"); 
}); 
Problemi correlati