2010-11-22 20 views
9

Problema:
Cargar js archivos de forma asíncrona, a continuación, comprobar para ver si el DOM se carga antes de la devolución de llamada desde la carga de los archivos se ejecuta.carga JavaScript asíncrono, a continuación, comprobar DOM cargado antes de ejecutar devolución de llamada

editar: No usamos jQuery; usamos Prototipo
editar: agregó más comentarios al ejemplo de código.

Hola chicos,

estoy tratando de cargar todos mis archivos js de forma asíncrona con el fin de evitar que se bloquea el resto de la página. Pero cuando los scripts se cargan y se llama a la devolución de llamada, necesito saber si el DOM se ha cargado o no, así que sé cómo estructurar la devolución de llamada. Vea a continuación:

//load asynchronously 
(function(){ 
     var e = document.createElement('script'); 
     e.type = "text/javascript"; 
     e.async = true; 
     e.src = srcstr; 
     // a little magic to make the callback happen 
     if(navigator.userAgent.indexOf("Opera")){ 
      e.text = "initPage();"; 
     }else if(navigator.userAgent.indexOf("MSIE")){ 
      e.onreadystatechange = initPage; 
     }else{ 
      e.innerHTML = "initPage();"; 
     } 
     // attach the file to the document 
     document.getElementsByTagName('head')[0].appendChild(e); 
})(); 

initPageHelper = function(){ 
    //requires DOM be loaded 
} 

initPage = function(){ 
    if(domLoaded){ // if dom is already loaded, just call the function 
     initPageHelper(); 
    }else{ //if dom is not loaded, attach the function to be run when it does load 
     document.observe("dom:loaded", initPageHelper); 
    } 
} 

La devolución de llamada se llama correctamente debido a un poco de magia detrás de las escenas que se pueden aprender acerca de esta charla Google: http://www.youtube.com/watch?v=52gL93S3usU&feature=related

¿Cuál es el más fácil, el método multi-navegador por preguntar si el DOM ya ha cargado?

EDIT
Aquí está la solución completa que fui.
Incluí el prototipo y el gestor de scripts asíncrono utilizando el método normal. La vida es mucho más fácil con el prototipo, así que estoy dispuesto a bloquear ese guión.

<script type="text/javascript" src="prototype/prototype.js"></script> 
<script type="text/javascript" src="asyncLoader.js"></script> 

Y, de hecho, en mi código que minified los dos archivos anteriores y ponerlos juntos en un solo archivo para minimizar el tiempo de transferencia y peticiones http.

Luego defino lo que quiero ejecutar cuando se carga el DOM, y luego llamo a la función para cargar los otros scripts.

<script type="text/javascript"> 
    initPage = function(){ 
    ... 
    } 
</script> 
<script type="text/javascript"> 
    loadScriptAsync("scriptaculous/scriptaculous.js", initPage); 
    loadScriptAsync("scriptaculous/effects.js", initPage); 
    loadScriptAsync("scriptaculous/controls.js", initPage); 
     ... 
    loadScriptAsync("mypage.js", initPage); 
</script> 

Del mismo modo, las solicitudes anteriores están en realidad comprimidas en una httpRequest utilizando un minificador. Se dejan separados aquí para facilitar la lectura. Hay un fragmento en la parte inferior de esta publicación que muestra cómo se ve el código con el minificador.

El código para asyncLoader.js es la siguiente:

/** 
* Allows you to load js files asynchronously, with a callback that can be 
* called immediately after the script loads, OR after the script loads and 
* after the DOM is loaded. 
* 
* Prototype.js must be loaded first. 
* 
* For best results, create a regular script tag that calls a minified, combined 
* file that contains Prototype.js, and this file. Then all subsequent scripts 
* should be loaded using this function. 
* 
*/ 
var onload_queue = []; 
var dom_loaded = false; 
function loadScriptAsync(src, callback, run_immediately) { 
     var script = document.createElement('script'); 
     script.type = "text/javascript"; 
     script.async = true; 
     script.src = src; 
     if("undefined" != typeof callback){ 
      script.onload = function() { 
       if (dom_loaded || run_immediately) 
        callback(); 
       else 
        onload_queue.push(callback); 
       // clean up for IE and Opera 
       script.onload = null; 
       script.onreadystatechange = null; 
      }; 

      script.onreadystatechange = function() { 
      if (script.readyState == 'complete'){ 
       if (dom_loaded || run_immediately) 
        callback(); 
       else 
        onload_queue.push(callback); 
       // clean up for IE and Opera 
       script.onload = null; 
       script.onreadystatechange = null; 
      }else if(script.readyState == 'loaded'){ 
       eval(script); 
       if (dom_loaded || run_immediately) 
         callback(); 
       else 
        onload_queue.push(callback); 
       // clean up for IE and Opera 
       script.onload = null; 
       script.onreadystatechange = null; 
      } 
      }; 
     } 
     var head = document.getElementsByTagName('head')[0]; 
     head.appendChild(script); 
} 
document.observe("dom:loaded", function(){ 
    dom_loaded = true; 
    var len = onload_queue.length; 
    for (var i = 0; i < len; i++) { 
     onload_queue[i](); 
    } 
    onload_queue = null; 
}); 

I añadido la opción de ejecutar una secuencia de comandos inmediatamente, si tiene secuencias de comandos que no se basan en la página DOM estar totalmente cargado.

Las solicitudes minified realmente se ven como:

<script type="text/javascript" src="/min/?b=javascript/lib&f=prototype/prototype.js,asyncLoader.js"></script> 
<script type="text/javascript"> initPage = function(e){...}</script> 
<script type="text/javascript"> 
    srcstr = "/min/?f=<?=implode(',', $js_files)?>"; 
    loadScriptAsync(srcstr, initPage); 
</script> 

Están usando el plugin de: http://code.google.com/p/minify/

Gracias por la ayuda !!
-K

+0

que haría uso de eventos de jQuery $ (document) ready(). La forma más sencilla y cruzada que conozco. – Yoni

+0

Puede usar un marcador que puede establecer cuando DOM está cargado y buscar ese indicador en su callback fn. – Rajat

+0

puede marcar una respuesta como aceptada si su problema fue resuelto. – galambalazs

Respuesta

9

Lo que necesita es una simple cola de onload funciones. También por favor evitar el rastreo del navegador ya que es inestable y no es a prueba de futuro. Para el código fuente completo ver el uso de la prueba [Demo]

var onload_queue = []; 
var dom_loaded = false; 

function loadScriptAsync(src, callback) { 
    var script = document.createElement('script'); 
    script.type = "text/javascript"; 
    script.async = true; 
    script.src = src; 
    script.onload = script.onreadystatechange = function() { 
    if (dom_loaded) 
     callback(); 
    else 
     onload_queue.push(callback); 
    // clean up for IE and Opera 
    script.onload = null; 
    script.onreadystatechange = null; 
    }; 
    var head = document.getElementsByTagName('head')[0]; 
    head.appendChild(script); 
} 

function domLoaded() { 
    dom_loaded = true; 
    var len = onload_queue.length; 
    for (var i = 0; i < len; i++) { 
    onload_queue[i](); 
    } 
    onload_queue = null; 
}; 

// Dean's dom:loaded code goes here 
// do stuff 
domLoaded(); 

loadScriptAsync(
    "http://code.jquery.com/jquery-1.4.4.js", 
    function() { 
     alert("script has been loaded"); 
    } 
); 
+0

, pero ¿qué sucede si la devolución de llamada asíncrona vuelve después de que se haya cargado dom? – Ken

+0

el único problema con esta actualización es que también quería cargar la biblioteca prototipo de forma asíncrona, lo que significa que document.observe() no estará disponible. – Ken

+0

eso no es problema, solo use http://dean.edwards.name/weblog/2006/06/again/ para 'dom: loaded', incluso jQuery usa una versión modificada de esto. – galambalazs

0

Siempre puede poner su script de cargador inicial en la parte inferior, justo antes de la etiqueta de cierre del cuerpo.

+0

esto no funcionará, ya que quiero que mis scripts comiencen a cargarse de manera asíncrona en la parte superior de la página – Ken

+0

, en la parte inferior puede agregar una etiqueta de script que simplemente establezca una variable global "var loaded = true;" o similar. O podría tener un elemento con una identificación, y puedes verificar que ese elemento exista. – rob

+0

¿Y por qué quieres que se carguen en la parte superior? Si simplemente los coloca en la parte inferior, el analizador examinará el html, cargará las imágenes sin bloqueos y luego tendrá problemas con sus javascripts mientras sigue mostrando el resto de la página. Y como parece, tus guiones necesitan un dom cargado, ¿por qué juguetear con eso? Un simple $ (documento) .observe ("dom: loaded", function() {...}) hará el trabajo. – philgiese

Cuestiones relacionadas