2010-10-17 28 views
5

Quello che sto cercando di fare è includere dinamicamente uno o più file js da Javascript. So che ci sono un sacco di tecniche per questo, ma sto cercando una soluzione che rispetti queste regole:Include JavaScript in modo dinamico e aspetta

1) Non utilizzare alcun framework o librerie JS (jQuery, Prototype, ecc.).

2) Lo script deve interrompere l'esecuzione finché il file js esterno non viene completamente caricato e analizzato dal browser.

Il razionale # 2 è il file js esterno includerà le definizioni di funzione che devono essere utilizzate immediatamente dopo l'inclusione dello script. Il più delle volte il browser non ha avuto il tempo di analizzare il file js prima di iniziare a chiamare quelle funzioni.

Non mi dispiace utilizzando un callback per sapere quando lo script è terminato il caricamento, ma:

1) Io non so in anticipo come molti script stanno per essere inclusi in modo dinamico, in modo da don Non voglio e non posso scrivere un gruppo di callback annidati. Devo solo sapere quando hanno finito di caricare.

2) Sto dicendo che il tentativo di utilizzare qualche tipo di evento "load" per attivare la funzione callback potrebbe non funzionare se il browser ha memorizzato nella cache il codice JavaScript.

Modifica Avrei dovuto menzionare dall'inizio che questo è per un sistema di plugin, ma volevo mantenere la mia domanda/risposta abbastanza generica da essere utile ad altre persone.

Gli utenti definiscono quali plug-in si desidera caricare in un array.

plugins = [ 'FooPlugin', 'BarPlugin' ]; 

avrei quindi scorrere l'array, e caricare lo script js per ogni plugin:

for(var i = 0; i < plugins.length; i++) { 
    loadScript('plugins/' + plugins[i] + '.js'); 
} 

Ogni plugin si spinge sulla matrice loaded_plugins (Questo è un esempio di FooPlugin.js)

load_plugins.push({ 
    name: 'FooPlugin', 
    // Other plugin methods and properties here 
}); 
+0

Non sono sicuro di questa risposta, quindi inseriscilo come commento. Hai provato a usare AJAX per caricare il contenuto del javascript in una variabile lato client, quindi usando 'eval()'? Poiché bisognava aspettare che AJAX restituisse un messaggio 200/OK, si sa che l'intero script è stato caricato, e 'eval()' non dovrebbe passare alla riga successiva finché non ha terminato di eseguire l'intero script. – stevendesu

+0

È possibile determinare quali script sono necessari durante il caricamento della pagina? Se è così, allora la soluzione di @ Paul è l'ideale. In caso contrario, la soluzione dipenderà dai browser che si desidera supportare e dal fatto che gli script si trovino nello stesso dominio della pagina. –

+0

@steven_desu - Le funzioni/variabili nello script caricato non dovrebbero finire nel contesto in cui è stato chiamato eval()? Significa che non sarebbero disponibili al di fuori della funzione che utilizzava eval. – mellowsoon

risposta

3

Funziona su browser incrociato, a partire da IE6.

document.loadScript = function (src, callback) { 
    function script(src, onload) { 
     var scriptTag = document.createElement('script'); 
     if (onload) scriptTag.onload = onload; 
     scriptTag.src = src; 
     scriptTag.type = 'text/javascript'; 
     return scriptTag; 
    } 
    function outer(tag) { 
     var d = document.createElement('div'); 
     d.appendChild(tag); 
     return d.innerHTML; 
    } 
    if (!(src instanceof Array)) src = [src]; 
    var i, scr, 
     callbackId = 'dynCall_'+Math.floor(Math.random()*89999+10000); 
     counter = src.length, 
     call = function() { if (--counter == 0) callback(); }; 
    if (!document.body) { 
     window[callbackId] = function() { 
      delete window[callbackId]; 
      if (callback instanceof Function) callback(); 
     }; 
     for (i=0; i<src.length; i++) document.write(outer(script(src[i]))); 
     document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>'); 
     return; 
    } 
    for (i=0; i<src.length; i++) document.body.appendChild(script(src[i], call)); 
}; 

minified/offuscato:

(function(){document.loadScript=function(src,callback){function script(src,onload){var scriptTag=document.createElement('script');if(onload)scriptTag.onload=onload;scriptTag.src=src;scriptTag.type='text/javascript';return scriptTag}function outer(tag){var d=document.createElement('div');d.appendChild(tag);return d.innerHTML}if(!(src instanceof Array))src=[src];var i,scr,callbackId='dynCall_'+Math.floor(Math.random()*89999+10000);counter=src.length,call=function(){if(--counter==0)callback()};if(!document.body){window[callbackId]=function(){delete window[callbackId];if(callback instanceof Function)callback()};for(i=0;i<src.length;i++)document.write(outer(script(src[i])));document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>');return}for(i=0;i<src.length;i++)document.body.appendChild(script(src[i],call))};document.loadScript.toString=function(){return'function loadScript() { [obfuscated] }'}})(); 

rapida spiegazione dei rami:

Se il tag script chiamando CaricaScript è all'interno della testa o del corpo del documento, document.body sarà indefinito, come il DOM non è ancora in Pertanto, non possiamo usare il metodo standard per aggiungere un tag, e dobbiamo usare doc write. Questo ha il vantaggio che i tag che scriviamo avverranno in ordine necessariamente sequenziale, ma lo svantaggio è che dobbiamo avere una funzione di callback dell'oscilloscopio globale.

Nel frattempo, se abbiamo document.body, siamo d'oro per farlo nel modo giusto (-ish - facciamo sacrifici quando non ci sono librerie in giro per aiutarlo a fare bene-er, da cui il .onload(). non è che stiamo per lanciare molti eventi su un tag script, quindi sarà probabilmente OK.) Lo svantaggio (forse?) è che tutti gli script vengono caricati in modo asincrono, quindi è necessario avere un conto alla rovescia mentre essi caricare.

+0

Giusto per essere chiari ... è lo scr.onload che verrà ancora chiamato se il browser ha lo script nella cache, e in realtà non lo sta caricando? – mellowsoon

+0

Il scr.onload verrà chiamato, memorizzato nella cache o no, purché l'evento onload sia impostato * prima * l'attributo src sia. In caso contrario, la cache si blocca prima del successivo comando JS. Aggiuntivo: potresti voler estrarre lo script che è adesso; Ho aggiunto il supporto per più script. – Fordi

+0

Sembra che la proprietà outerHTML non sia supportata da Firefox. – mellowsoon

2

la soluzione più semplice (e più affidabile) che conosco è di utilizzare semplicemente document.write per inserire un nuovo tag script nel documento (o come molti come è necessario, ovviamente):

<script type="text/javascript">document.write('<' + 'script src="/assets/js/file.js" type="text/javascript"><\/script>');</script> 

dei browser (afaik) analizzare & eseguire script nell'ordine specificato nel documento dai tag di script, anche quando aggiunto in questo modo. Quindi dovresti essere bravo se metti il ​​resto del codice che vuoi eseguire in un nuovo tag script separato.

EDIT: Ecco un (molto) more elaborate approach, che a sua base sta usando "elem = document.createElement ('script')", seguito da ascoltando elem.onreadystatechange (per IE) e elem.onload. Carica un intero batch di script in parallelo, quindi attiva una richiamata quando sono tutti lì. L'ho usato in passato, quando i browser continuavano a caricare principalmente gli script uno alla volta per velocizzare il caricamento (è basato su uno script con scopi simili da qualche parte là fuori). Esempio di utilizzo:

Loader.script([ 
    './js/jquery/jquery.jmap.js', 
    './js/jquery/jquery.getUrlParam.js', 
    './js/jquery/jquery.form.js', 
    './js/jquery/jquery.gettext.js', 
    './js/jquery/jquery.scrollTo.js', 
    './js/jquery/jquery.prettydate.js' 
    ], { complete: function() { 
    // do your thing 
    } 
}) 
+1

document.write è solo una soluzione valida durante il caricamento iniziale della pagina. Usandolo in qualsiasi momento, sovrascriverà l'intera pagina. Il problema è un po 'più complicato di così. http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml spiega in modo dinamico il caricamento di javascript, ma questo non è abbastanza intelligente da rendersi conto quando ha finito di eseguire lo script, anche. – stevendesu

+0

Sì, buono; L'ho usato solo direttamente nella testa, un po 'di tempo fa. – Paul

+0

Il secondo approccio è compatibile con più browser? – mellowsoon

0

Alcuni possibili approcci dalla parte superiore della mia testa. Ho intenzione di scrivere i passaggi, non il codice.

Opzione 1

  1. carico il file JS ponendolo in l'elemento con un tag.
  2. Subito dopo attivare un setTimout() per verificare la presenza di una variabile nel file JS caricato.
  3. Ripeti # 2 fino a quando la variabile è presente o finché non viene raggiunto un maxCounter (maxCounter = 10) in modo da non rimanere lì per sempre.
  4. Una volta che la variabile è presente, passare al file JS successivo e ricominciare dal passaggio 1.

Opzione 2

  1. Utilizzare un approccio Ajax.
  2. Chiamare il file JS utilizzando Ajax.
  3. Aggiungere i risultati alle istruzioni di utilizzo nell'articolo seguente.
  4. setTimeout() funzione per pochi secondi e processo di script O controllare la variabile simile all'opzione # 1 e ripetere il timeout fino a quando la variabile è presente.

di riferimento questo articolo per entrambi gli approcci: http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS

La parte più difficile è che si sta chiedendo il vostro codice di sapere quando ogni file JS viene caricato e processato dal browser. Dal momento che computer e browser variano, il tempo necessario per elaborare un file JS può variare, rendendo difficile il tracciamento senza una logica nel file JS.

io preferirei un approccio come questo:

. Crea un tag di richiesta Javascript, ajax, qualunque cosa su un server in grado di elaborare la logica dinamica (php, java, .NET, ecc.).

. Passa una funzione di callback già esistente nella pagina e conosce quanti file JS aggiuntivi devi conoscere. Anche includere un parametro di indice per il callback (qualcosa come myCallback (5)) in modo da sapere dove ti trovi.

1-2a. Si può semplicemente aggiungere la funzione di callback a tutti i file JS manualmente e lasciare che la funzione gestisca la coda JS senza che i file JS abbiano altra conoscenza oltre alla funzione di callback.

. Consenti al server di aggiungere la richiamata al file JS che viene restituito.

. La funzione di callback verrà attivata una volta elaborato il JS e gestirà il caricamento di file JS aggiuntivi per te.

Che ne dici?

Problemi correlati