web-dev-qa-db-fra.com

Comment détecter les erreurs sur la page 404 en utilisant JavaScript?

J'ai une page HTML où plusieurs fichiers JavaScript, CSS et images sont référencés. Ces références sont injectées dynamiquement et l'utilisateur peut copier manuellement la page HTML et les fichiers de support sur une autre machine.

Si des fichiers JS ou CSS sont manquants, le navigateur se plaint dans la console. Par exemple:

Fichier d'erreur GET: /// E: /SSC_Temp/html_005/temp/Support/jquery.js

J'ai besoin d'une manière ou d'une autre de ces erreurs qui me sont signalées dans le code JavaScript intégré de la page HTML afin de pouvoir demander à l'utilisateur de vérifier au préalable que les fichiers de support sont copiés correctement.

Il y a l'événement window.onerror qui vient de m'informer qu'il y a une erreur JS sur la page telle qu'une erreur de syntaxe inattendue, mais qu'elle ne se déclenche pas en cas d'erreur 404 Introuvable . Je souhaite vérifier cette condition dans le cas de tout type de ressource, y compris CSS, JS et images.

Je n'aime pas utiliser jQuery AJAX pour vérifier que le fichier existe physiquement - la surcharge d'E/S est coûteuse pour chaque chargement de page.

Le rapport d'erreur doit contenir le nom du fichier manquant pour que je puisse vérifier si le fichier est core ou facultatif.

Des idées?

19
Santoo

Pour capturer tous les événements error de la page, vous pouvez utiliser addEventListener avec l'argument useCapture défini sur true. La raison pour laquelle window.onerror ne le fera pas est qu’elle utilise la phase d’événement bulle et que les événements error que vous souhaitez capturer ne sont pas masqués.

Si vous ajoutez le script suivant à votre code HTML avant de charger du contenu externe, vous devriez pouvoir capturer tous les événements error, même lors du chargement hors connexion.

<script type="text/javascript">
window.addEventListener('error', function(e) {
    console.log(e);
}, true);
</script>

Vous pouvez accéder à l'élément qui a provoqué l'erreur via e.target. Par exemple, si vous voulez savoir quel fichier n'a pas été chargé sur une balise img, vous pouvez utiliser e.target.src pour obtenir l'URL dont le chargement a échoué.

REMARQUE: Techniquement, cela ne détectera pas le code d'erreur, mais si le chargement de l'image échoue, son comportement technique est le même quel que soit le code d'état. En fonction de votre configuration, cela sera probablement suffisant, mais par exemple, si un 404 est renvoyé avec une image valide, il ne déclenchera pas d’événement d’erreur.

42
Alexander O'Mara

J'ai mis en place le code ci-dessous en JavaScript pur, testé et cela fonctionne. Tout le code source (html, css, et Javascript) + images et exemple de police est ici: sur github .

Le premier bloc de code est un objet avec des méthodes pour des extensions de fichier spécifiques: html et css. Le second est expliqué ci-dessous, mais voici une brève description.

Il fait ce qui suit:

  • la fonction check_file prend 2 arguments: un chemin de chaîne et une fonction de rappel.
  • récupère le contenu du chemin donné
  • obtient l'extension de fichier (ext) du chemin donné
  • appelle la méthode d'objet srcFrom [ext] qui renvoie un tableau de chemins d'accès relatifs référencés dans le contexte de chaîne par src, href, etc.
  • effectue un appel synchrone à chacun de ces chemins dans le tableau de chemins
  • s'arrête en cas d'erreur et renvoie le message d'erreur HTTP et le chemin qui a provoqué un problème; vous pouvez donc l'utiliser pour d'autres problèmes, tels que 403 (interdit), etc.

Pour des raisons pratiques, il résout les noms de chemin relatifs et ne se soucie pas du protocole utilisé (http ou https, cela ne pose pas de problème). Il nettoie également le DOM après l'analyse du code CSS.

var srcFrom = // object
{
    html:function(str)
    {
        var prs = new DOMParser();
        var obj = prs.parseFromString(str, 'text/html');
        var rsl = [], nds;

        ['data', 'href', 'src'].forEach(function(atr)
        {
            nds = [].slice.call(obj.querySelectorAll('['+atr+']'));
            nds.forEach(function(nde)
            { rsl[rsl.length] = nde.getAttribute(atr); });
        });

        return rsl;
    },

    css:function(str)
    {
        var css = document.createElement('style');
        var rsl = [], nds, tmp;

        css.id = 'cssTest';
        css.innerHTML = str;
        document.head.appendChild(css);
        css = [].slice.call(document.styleSheets);

        for (var idx in css)
        {
            if (css[idx].ownerNode.id == 'cssTest')
            {
                [].slice.call(css[idx].cssRules).forEach(function(ssn)
                {
                    ['src', 'backgroundImage'].forEach(function(pty)
                    {
                        if (ssn.style[pty].length > 0)
                        {
                            tmp = ssn.style[pty].slice(4, -1);
                            tmp = tmp.split(window.location.pathname).join('');
                            tmp = tmp.split(window.location.Origin).join('');
                            tmp = ((tmp[0] == '/') ? tmp.substr(1) : tmp);
                            rsl[rsl.length] = tmp;
                        }
                    });
                });

                break;
            }
        }

        css = document.getElementById('cssTest');
        css.parentNode.removeChild(css);
        return rsl;
    }
};

Et voici la fonction qui récupère le contenu du fichier et appelle la méthode d'objet ci-dessus en fonction de l'extension de fichier:

function check_file(url, cbf)
{
    var xhr = new XMLHttpRequest();
    var uri = new XMLHttpRequest();

    xhr.open('GET', url, true);

    xhr.onload = function()
    {
        var ext = url.split('.').pop();
        var lst = srcFrom[ext](this.response);
        var rsl = [null, null], nds;

        var Break = {};

        try
        {
            lst.forEach(function(tgt)
            {
                uri.open('GET', tgt, false);
                uri.send(null);

                if (uri.statusText != 'OK')
                {
                    rsl = [uri.statusText, tgt];
                    throw Break;
                }
            });
        }
        catch(e){}

        cbf(rsl[0], rsl[1]);
    };

    xhr.send(null);
}

Pour l'utiliser, appelez-le simplement comme ceci:

var uri = 'htm/stuff.html';    // html example

check_file(uri, function(err, pth)
{
    if (err)
    { document.write('Aw Snap! "'+pth+'" is missing !'); }
});

N'hésitez pas à commenter et éditer à votre guise, je l'ai fait c'est pressé, donc ce n'est peut-être pas si joli :)

4
argon

vous pouvez utiliser les attributs onload et onerror pour détecter l'erreur

par exemple, lors du chargement du code html suivant, il génère une alerte error1 et error2 vous pouvez appeler votre propre fonction, par exemple onerror(logError(this);), et les enregistrer dans un tableau et, une fois la page entièrement chargée, la publication ne comporte qu'un seul appel Ajax.

<html>
    <head>
        <script src="file:///SSC_Temp/html_005/temp/Support/jquery.js" onerror="alert('error1');" onload="alert('load');" type="text/javascript" ></script>
    </head>
    <body>
        <script src="file:///SSC_Temp/html_005/temp/Support/jquery.js" onerror="alert('error2');" onload="alert('load');" type="text/javascript" ></script>
    </body>
</html>
3
Zeronex

Vous pouvez utiliser XMLHttpRequest pour les fichiers.

var oReq = new XMLHttpRequest();
oReq.addEventListener("error", transferFailed, false);

function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}

client.open("GET", "Unicorn.xml");
client.send();

et utilisez la classe Image pour les images.

var img1 = new Image();
img1.src = 'http://yourdomain.net/images/onethatdoesnotexist.gif';
img1.onerror = function () { alert( 'Image not loaded' ); };
0
Ruslan López

@ alexander-omara a donné la solution.

Vous pouvez même l'ajouter à de nombreux fichiers, mais le gestionnaire de fenêtres peut/devrait être ajouté une fois.

J'utilise le modèle singleton pour y parvenir:

some_global_object = {
  error: (function(){
     var activate = false;
     return function(enable){
        if(!activate){
           activate = true;
           window.addEventListener('error', function(e){
              // maybe extra code here...
              // if(e.target.custom_property)
              // ...
           }, true);
        }
        return activate;
     };
  }());

Désormais, quel que soit le contexte, appelez-le autant de fois que vous le souhaitez, car le gestionnaire est associé uniquement à une fois:

some_global_object.error();
0
centurian