2009-12-10 21 views
5

Tengo una función javascript que estoy escribiendo que se usa para incluir un archivo JS externo, pero solo una vez. La razón por la que necesito una función así es porque se llama cuando se carga contenido a través de AJAX y necesito ejecutar código específico de la página para ese contenido (no, solo usar .live no lo cubrirá).Dinámicamente incluyendo archivos javascript una sola vez

Aquí está mi intento, acortado por razones de brevedad:

$.include_once = function(filename) { 
    if ($("script[src='" + filename + "']").length === 0) { 
     var $node = $("<script></script>") 
      .attr({ 
       src : filename, 
       type : "text/javascript" 
      }) 
     ; 
     $(document.body).append($node); 
    } 
}; 

Esto funciona bien: la función se llama, se carga el archivo externo, y ese archivo se ejecuta cuando se carga. Perfecto.

El problema es que siempre volverá a cargar ese archivo externo: la consulta que estoy usando para verificar la presencia del script ¡no encuentra nada!

Al depurar esto, añade algunas líneas:

alert($("script").length);  // alerts: 4 
$(document.body).append($node); 
alert($("script").length);  // alerts: 4 

mirando en la fuente dinámica (en la ficha HTML de Firebug), no puedo encontrar la etiqueta de script en absoluto.

Sé que podría mantener una serie de archivos que he incluido anteriormente, pero esperaba ir con un método como este, que (si funciona), parece un poco más robusto, ya que no todos los archivos JS se están incluyendo de esta manera.

¿Alguien puede explicar el comportamiento visto en este segundo fragmento?

Respuesta

5

jQuery es un poco tonto en este caso; no hace nada de lo que esperarías. Cuando se hace esto append($node) jQuery:

jQuery.ajax({ 
    url: $node.src, 
    async: false, 
    dataType: "script" 
}) 

Woops! Para los archivos locales (por ejemplo, en el mismo dominio) jQuery realiza un estándar XMLHttpRequest de los js de cuerpo de archivo, y procede a "eval" por un proceso complicado conjunto de la creación de una etiqueta <script> (otra vez!) Y la configuración es contenidos a su cuerpo de archivo .js. Esto es para simular eval pero en el contexto global.

Para los archivos de varios dominios, ya que no puede realizar el estándar XMLHttpRequest debido a la política del mismo dominio, jQuery, una vez más crea un elemento <script> y lo inserta en <head>.

En ambos casos los locales y entre dominios anteriores jQuery finalmente consigue alrededor a hacer esto:

head.removeChild(script); 

Y auges su .length cheque! Gorrón.

Así que a su problema, no moleste a jQuery con esto. Solo haga

document.getElementsByTagName('head')[0] 
    .appendChild(
    document.createElement('script') 
) 
    .src = filename; 

Que hará lo que usted esperaría, en particular, wrt solicitándolo más tarde.

+0

Excelente respuesta. Pensé que jQuery estaba siendo demasiado listo para sí mismo o algo así. – nickf

+0

@nickf: Me siento así por la mayoría de las entrañas de jQuery (siendo demasiado inteligente); supongo que hay muchos males necesarios. –

3

Estás tratando de resolver un problema que ya ha sido resuelto varias veces. Pruebe LazyLoad por ejemplo. También hay complementos similares para jQuery.

0

En lugar de establecer el atributo de origen de la secuencia de comandos, establezca el atributo "texto" de la etiqueta de secuencia de comandos. Esto funciona en todos los modernos navegadores (la aplicación donde uso eso en la práctica no es compatible con IE6, así que no sé acerca de este creep ...).

En la práctica, se vería así (TIENE QUE agregar código para omitir la doble inclusión en usted mismo, por ejemplo, una matriz simple de todos los scripts ya cargados, aunque es muy específico de la aplicación y ¿por qué debería cargar el código dos veces? omitir código de doble carga ...):

var script_source_code_string = <some dynamically loaded script source code>; 
var $n = $("<script></script>"); 
$n.get(0).text = script_source_code_string; 
$(document.body).append($n); 

O aún más simple (sin jQuery, mi código en esta etapa no sabe jQuery, también puede ser cargado dinámicamente):

var script_source_code_string = <some dynamically loaded script source code>; 
var s = document.createElement('script'); 
document.getElementsByTagName('head')[0].appendChild(s); 
s.text = script_source_code_string; 
Cuestiones relacionadas