Charge javascript asynchrone, puis vérifier DOM chargé avant l'exécution de rappel
Problème:
Charger des fichiers js de manière asynchrone, puis vérifiez si le dom est chargé avant le rappel de chargement des fichiers est exécuté.
edit: Nous n'utilisons pas de jQuery, Prototype.
edit: ajouté plus de commentaires pour l'exemple de code.
Hé les gars,
Je suis en train de charger tous mes fichiers js asynchrone afin de les empêcher de bloquer le reste de la page. Mais quand les scripts de la charge et de la fonction de rappel est appelée, j'ai besoin de savoir si le DOM est chargé ou pas, donc je sais comment la structure de la fonction de rappel. Voir ci-dessous:
//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 fonction de rappel est appelée correctement à cause de la magie derrière les scènes que vous pouvez apprendre à partir de ce Google talk: http://www.youtube.com/watch?v=52gL93S3usU&feature=related
Ce qui est le plus facile, la croix-navigateur méthode pour demander si le DOM est chargé déjà?
MODIFIER
Voici la solution complète, je suis allé avec.
J'ai inclus prototype et de l'asynchrone chargeur de script à l'aide de la méthode normale. La vie est tellement plus facile de prototype, donc, je suis prêt à bloc pour ce script.
<script type="text/javascript" src="prototype/prototype.js"></script>
<script type="text/javascript" src="asyncLoader.js"></script>
Et en fait, dans mon code, j'ai minimisé les deux fichiers ci-dessus et les mettre ensemble dans un seul fichier afin de réduire les temps de transfert et les requêtes http.
Puis-je définir ce que je veux exécuter lorsque le DOM, et ensuite appeler la fonction pour charger les autres 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>
De même, les demandes ci-dessus sont en fait compressés en un seul httpRequest à l'aide d'un minifier. Ils sont laissés séparé ici pour des raisons de lisibilité. Il y a un extrait en bas de ce post montrant à quoi ressemble le code avec le minifier.
Le code pour asyncLoader.js est la suivante:
/**
* 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;
});
J'ai ajouté la possibilité d'exécuter un script immédiatement, si vous avez des scripts qui ne reposent pas sur la page de DOM d'être complètement chargé.
La minimisé les demandes effectivement ressembler:
<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>
Ils sont en utilisant le plugin à partir de: http://code.google.com/p/minify/
Merci pour l'aide!!
-K
Vous pouvez utiliser un drapeau que vous pouvez définir quand le DOM est chargé et vérifier que l'indicateur dans votre rappel fn.
vous pouvez marquer une réponse comme acceptée si votre problème a été résolu.
désolé @galambalazs, j'ai marqué maintenant.
OriginalL'auteur Ken | 2010-11-22
Vous devez vous connecter pour publier un commentaire.
Ce que vous avez besoin est d'une simple file d'attente de
onload
fonctions. Aussi s'il vous plaît éviter la détection de browser car il est instable et pas d'avenir. Pour le code source complet voir le [Démo]Test d'utilisation
le seul problème avec cette mise à jour est que j'ai aussi voulu charger le prototype de la bibliothèque de manière asynchrone, ce qui signifie que le document.observer() ne sera pas disponible.
c'est pas un problème, il suffit d'utiliser dean.edwards.nom/blog/2006/06/nouveau pour
dom:loaded
, même jQuery utilise une version modifiée de ce.script.onload = script.onreadystatechange = function()... cette fonction sera appelée deux fois à l'Opéra. vous devez définir un indicateur et vérifiez que la fonction n'a pas été lancé.
Actaully vous n'avez pas à. Il suffit de retirer les écouteurs d'événement par invalide. Il empêche aussi l'IE fuites de mémoire! Mise à jour du code + démo.
OriginalL'auteur gblazex
Vous pouvez toujours mettre vos initiales chargeur de script en bas, juste avant la balise de fermeture body.
eh bien, en bas, vous pouvez ajouter une balise script qui définit simplement une variable globale "var loaded=true;" ou similaire. Ou il pourrait avoir un élément avec un id, et vous pouvez vérifier que l'élément existant.
Et pourquoi voulez-vous être à charge en haut? Si vous il suffit de les placer dans le fond, l'analyseur va aller sur le code html, de charger les images non-blocage et puis battre avec vos javascripts et diffuser le reste de la page. Et comme il semble, que vos scripts besoin d'un chargé dom toute façon, pourquoi se tripoter? Un simple $(document).observer("dom:loaded", function() {...}) fera le travail.
Parce que dès que le dom est chargé, des morceaux de celui-ci peut être affiché. J'ai besoin de l'contrôles de être au maximum de réactivité, et beaucoup d'entre eux sont en js. Donc je veux la js à transfert en mode asynchrone en même temps que le balisage, alors je veux les gestionnaires d'attaché dès que le dom est prêt à créer le maximum de réactivité. Je ne veux pas attendre pour transférer les fichiers jusqu'à ce que après le dom est chargé, et puis attacher les gestionnaires.
A de sens, bien que je doute que vous vraiment acheter quelque chose en ayant la charge initié en haut de la page contre le fond (à moins que la page est très lourde et lente à générer). Peu importe, vous pouvez le faire en appelant une fonction au bas de la page, et d'appeler la même fonction au bas de chaque fichier javascript qui est chargé. Seulement la dernière fois que la fonction est appelée, il sera de déclencher quoi que ce soit....toutes les autres fois, elle sera tenue d'un comte. Donc, il n'a pas d'importance si le dom est chargé en premier, ou les fichiers js....la dernière fois grâce à elle déclenche quel que soit le comportement dont vous avez besoin.
OriginalL'auteur rob