142 votes

Comment savoir si le chargement d'une balise <script> a échoué ?

J'ajoute dynamiquement <script> aux balises <head> et j'aimerais pouvoir dire si le chargement a échoué d'une manière ou d'une autre : une erreur 404, une erreur de script dans le script chargé, etc.

Dans Firefox, cela fonctionne :

var script_tag = document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', 'http://fail.org/nonexistant.js');
script_tag.onerror = function() { alert("Loading failed!"); }
document.getElementsByTagName('head')[0].appendChild(script_tag);

Cependant, cela ne fonctionne pas dans IE ou Safari.

Quelqu'un connaît-il un moyen de faire fonctionner ceci dans des navigateurs autres que Firefox ?

(Je ne pense pas qu'une solution qui nécessite de placer un code spécial dans les fichiers .js soit une bonne solution. C'est inélégant et peu flexible).

4 votes

Grand site web, chargement automatique des dépendances pour le contenu chargé par ajax. if +Les sondages sont des éléments ennuyeux que je ne veux pas avoir à mettre dans tous les JS.

2 votes

Vous pourriez également vouloir vérifier l'échec de chargement lors d'une requête JSONP utilisant l'injection de la balise script...

2 votes

42voto

user505567 Points 91

Si vous ne vous intéressez qu'aux navigateurs html5, vous pouvez utiliser l'événement error (étant donné qu'il ne s'agit que de la gestion des erreurs, il devrait être acceptable de ne le prendre en charge que sur les navigateurs de la prochaine génération pour des raisons de simplicité).

De la spécification :

Si la valeur de l'attribut src est le chaîne vide ou si elle n'a pas pu être résolu, l'agent utilisateur doit mettre en file d'attente une tâche pour déclencher un événement simple nommé erreur à l'élément, et interrompre ces étapes.

~

Si le chargement a donné lieu à une erreur (par exemple, une erreur DNS ou une erreur HTTP 404). erreur) l'exécution du bloc script doit simplement consister à déclencher un simple événement nommé error à l'élément.

Cela signifie que vous n'avez pas à effectuer de polling sujet à des erreurs et que vous pouvez le combiner avec l'attribut async et defer pour vous assurer que le script ne bloque pas le rendu de la page :

L'attribut defer peut être spécifié même si l'attribut async est spécifié, pour que les anciens navigateurs qui ne prennent en charge que defer (et non (et non async) se rabattent sur le comportement defer au lieu du comportement de blocage synchrone qui est le comportement par défaut.

En savoir plus http://www.w3.org/TR/html5/scripting-1.html#script

1 votes

Une idée de l'implémentation de JSONP, comme celle de jQuery, qui supporte cela ? Tout ce que j'ai lu ici sur la détection des échecs de chargement de JSONP dit que cela ne peut pas être détecté mais qu'un délai d'attente peut être une solution de contournement, et un délai d'attente a été ajouté à une version récente de JSONP. Il semble que cette réponse offre quelque chose de mieux que le délai d'attente, est-ce exact ?

1 votes

Exemple possible, s'il vous plaît ?

18 votes

<script src="nonexistent.js" onerror="alert('error!')"></script> violon

35voto

Diodeus Points 67946

Il n'y a pas d'événement d'erreur pour la balise script. Vous pouvez savoir quand elle réussit, et supposer qu'elle ne s'est pas chargée après un délai d'attente :

<script type="text/javascript" onload="loaded=1" src="....js"></script>

8 votes

L'écouteur "onload" sera activé même s'il y a une erreur javascript.

44 votes

Eh bien, oui si le fichier se charge et qu'il y a une erreur dans le fichier lui-même, mais si le fichier n'est pas servi, le onload ne se déclenchera jamais.

1 votes

Oui, c'est vrai, c'est pourquoi j'ai demandé à être plus précis sur le type d'erreur qu'il recherche.

21voto

Aram Kocharyan Points 8530

Le script d'Erwinus fonctionne très bien, mais n'est pas codé très clairement. J'ai pris la liberté de le nettoyer et de déchiffrer ce qu'il faisait. J'ai fait ces changements :

  • Noms de variables significatifs
  • Utilisation de prototype .
  • require() utilise une variable d'argument
  • Non alert() les messages sont renvoyés par défaut
  • Correction de quelques erreurs de syntaxe et de problèmes de portée que j'obtenais

Merci encore à Erwinus, la fonctionnalité elle-même est parfaite.

function ScriptLoader() {
}

ScriptLoader.prototype = {

    timer: function (times, // number of times to try
                     delay, // delay per try
                     delayMore, // extra delay per try (additional to delay)
                     test, // called each try, timer stops if this returns true
                     failure, // called on failure
                     result // used internally, shouldn't be passed
            ) {
        var me = this;
        if (times == -1 || times > 0) {
            setTimeout(function () {
                result = (test()) ? 1 : 0;
                me.timer((result) ? 0 : (times > 0) ? --times : times, delay + ((delayMore) ? delayMore : 0), delayMore, test, failure, result);
            }, (result || delay < 0) ? 0.1 : delay);
        } else if (typeof failure == 'function') {
            setTimeout(failure, 1);
        }
    },

    addEvent: function (el, eventName, eventFunc) {
        if (typeof el != 'object') {
            return false;
        }

        if (el.addEventListener) {
            el.addEventListener(eventName, eventFunc, false);
            return true;
        }

        if (el.attachEvent) {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    },

    // add script to dom
    require: function (url, args) {
        var me = this;
        args = args || {};

        var scriptTag = document.createElement('script');
        var headTag = document.getElementsByTagName('head')[0];
        if (!headTag) {
            return false;
        }

        setTimeout(function () {
            var f = (typeof args.success == 'function') ? args.success : function () {
            };
            args.failure = (typeof args.failure == 'function') ? args.failure : function () {
            };
            var fail = function () {
                if (!scriptTag.__es) {
                    scriptTag.__es = true;
                    scriptTag.id = 'failed';
                    args.failure(scriptTag);
                }
            };
            scriptTag.onload = function () {
                scriptTag.id = 'loaded';
                f(scriptTag);
            };
            scriptTag.type = 'text/javascript';
            scriptTag.async = (typeof args.async == 'boolean') ? args.async : false;
            scriptTag.charset = 'utf-8';
            me.__es = false;
            me.addEvent(scriptTag, 'error', fail); // when supported
            // when error event is not supported fall back to timer
            me.timer(15, 1000, 0, function () {
                return (scriptTag.id == 'loaded');
            }, function () {
                if (scriptTag.id != 'loaded') {
                    fail();
                }
            });
            scriptTag.src = url;
            setTimeout(function () {
                try {
                    headTag.appendChild(scriptTag);
                } catch (e) {
                    fail();
                }
            }, 1);
        }, (typeof args.delay == 'number') ? args.delay : 1);
        return true;
    }
};

$(document).ready(function () {
    var loader = new ScriptLoader();
    loader.require('resources/templates.js', {
        async: true, success: function () {
            alert('loaded');
        }, failure: function () {
            alert('NOT loaded');
        }
    });
});

4 votes

J'ai été contraint d'utiliser la fonction de jQuery $.getScript Cette fonction n'a pas fonctionné pour les scripts mis en cache dans MSIE8-, malheureusement.

0 votes

@ZathrusWriter c'est probablement une meilleure idée, merci de nous le faire savoir ! Vous pourriez probablement ajouter un int aléatoire à l'url dans ce code et cela supprimerait la mise en cache.

2 votes

Oui Aram, cela ferait certainement l'affaire, mais cela invaliderait aussi la mise en cache du navigateur, donc la surcharge dans ce cas n'en vaut probablement pas la peine ;)

17voto

Erwinus Points 1587

Je sais que c'est un vieux fil de discussion mais j'ai une solution intéressante pour vous (je pense). C'est copié d'une de mes classes, qui gère tous les trucs AJAX.

Lorsque le script ne peut pas être chargé, il définit un gestionnaire d'erreurs mais lorsque le gestionnaire d'erreurs n'est pas pris en charge, il se rabat sur un timer qui vérifie les erreurs pendant 15 secondes.

function jsLoader()
{
    var o = this;

    // simple unstopable repeat timer, when t=-1 means endless, when function f() returns true it can be stopped
    o.timer = function(t, i, d, f, fend, b)
    {
        if( t == -1 || t > 0 )
        {
            setTimeout(function() {
                b=(f()) ? 1 : 0;
                o.timer((b) ? 0 : (t>0) ? --t : t, i+((d) ? d : 0), d, f, fend,b );
            }, (b || i < 0) ? 0.1 : i);
        }
        else if(typeof fend == 'function')
        {
            setTimeout(fend, 1);
        }
    };

    o.addEvent = function(el, eventName, eventFunc)
    {
        if(typeof el != 'object')
        {
            return false;
        }

        if(el.addEventListener)
        {
            el.addEventListener (eventName, eventFunc, false);
            return true;
        }

        if(el.attachEvent)
        {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    };

    // add script to dom
    o.require = function(s, delay, baSync, fCallback, fErr)
    {
        var oo = document.createElement('script'),
        oHead = document.getElementsByTagName('head')[0];
        if(!oHead)
        {
            return false;
        }

        setTimeout( function() {
            var f = (typeof fCallback == 'function') ? fCallback : function(){};
            fErr = (typeof fErr == 'function') ? fErr : function(){
                alert('require: Cannot load resource -'+s);
            },
            fe = function(){
                if(!oo.__es)
                {
                    oo.__es = true;
                    oo.id = 'failed';
                    fErr(oo);
                }
            };
            oo.onload = function() {
                oo.id = 'loaded';
                f(oo);
            };
            oo.type = 'text/javascript';
            oo.async = (typeof baSync == 'boolean') ? baSync : false;
            oo.charset = 'utf-8';
            o.__es = false;
            o.addEvent( oo, 'error', fe ); // when supported

            // when error event is not supported fall back to timer
            o.timer(15, 1000, 0, function() {
                return (oo.id == 'loaded');
            }, function(){ 
                if(oo.id != 'loaded'){
                    fe();
                }
            });
            oo.src = s;
            setTimeout(function() {
                try{
                    oHead.appendChild(oo);
                }catch(e){
                    fe();
                }
            },1); 
        }, (typeof delay == 'number') ? delay : 1);  
        return true;
    };

}

$(document).ready( function()
{
    var ol = new jsLoader();
    ol.require('myscript.js', 800, true, function(){
        alert('loaded');
    }, function() {
        alert('NOT loaded');
    });
});

4 votes

...d'où vient jQuery ?

0 votes

Vous utilisez $('#'+os) et un ou deux autres. Vous n'avez pas indiqué dans votre réponse que vous utilisiez jQuery.

1 votes

Et aussi, c'est une solution multi-navigateurs ;-)

10voto

Luca Matteis Points 19338

Pour vérifier si le javascript dans nonexistant.js n'a pas retourné d'erreur, vous devez ajouter une variable à l'intérieur du fichier http://fail.org/nonexistant.js comme var isExecuted = true; et ensuite vérifier s'il existe lorsque la balise script est chargée.

Toutefois, si vous souhaitez uniquement vérifier que le nonexistant.js est retourné sans un 404 (ce qui signifie qu'il existe), vous pouvez essayer avec un isLoaded variable ...

var isExecuted = false;
var isLoaded = false;
script_tag.onload = script_tag.onreadystatechange = function() {
    if(!this.readyState ||
        this.readyState == "loaded" || this.readyState == "complete") {
        // script successfully loaded
        isLoaded = true;

        if(isExecuted) // no error
    }
}

Cela couvrira les deux cas.

1 votes

Cela fonctionne, mais ne fait pas vraiment ce que je veux : dans ce cas, un échec ne déclencherait jamais d'événement. Je ne veux pas avoir à interroger une variable et, si elle est toujours fausse après quelques secondes, déclencher le rappel en cas d'échec.

0 votes

Quel type d'erreur essayez-vous de récupérer ? Échec du chargement ? Échec de l'exécution du javascript dans nonexstant.js ? Erreur de réponse HTTP ? Veuillez être plus descriptif.

0 votes

Tout ce qui précède serait bien. Mais je tiens particulièrement à une erreur 404.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X