wake-up-neo.com

Wie erkennt man On-Page 404-Fehler mit JavaScript?

Ich habe eine HTML-Seite, auf der auf mehrere JavaScript-, CSS- und Bilddateien verwiesen wird. Diese Verweise werden dynamisch eingefügt und der Benutzer kann die HTML-Seite und die Unterstützungsdateien manuell auf einen anderen Computer kopieren.

Wenn JS oder CSS fehlen, beklagt sich der Browser in der Konsole. Zum Beispiel:

Fehler GET-Datei: /// E: /SSC_Temp/html_005/temp/Support/jquery.js

Ich muss diese Fehler irgendwie im Inline-JavaScript der HTML-Seite melden, damit ich den Benutzer auffordern kann, zunächst zu überprüfen, ob die Unterstützungsdateien korrekt kopiert wurden.

Es gibt das Ereignis window.onerror, das mich nur darüber informiert, dass auf der Seite ein JS-Fehler vorliegt, z. B. ein unerwarteter Syntaxfehler, der jedoch nicht ausgelöst wird, wenn der Fehler 404 Not Found auftritt. Ich möchte diesen Zustand bei jedem Ressourcentyp überprüfen, einschließlich CSS, JS und Bildern.

Ich verwende jQuery AJAX nicht gern, um zu überprüfen, ob die Datei physisch vorhanden ist - der E/A-Aufwand ist für jedes Laden der Seite teuer.

Der Fehlerbericht muss den Namen der Datei enthalten fehlt, damit ich überprüfen kann, ob die Datei ein Kern oder optional ist.

Irgendwelche Ideen?

19
Santoo

Um alle error-Ereignisse auf der Seite zu erfassen, können Sie addEventListener verwenden, wobei das useCapture-Argument auf true gesetzt ist. Der Grund, warum window.onerror dies nicht tut, liegt darin, dass er die Blasenereignisphase verwendet und die error-Ereignisse, die Sie erfassen möchten, keine Blasen bilden.

Wenn Sie Ihrem HTML-Code das folgende Skript hinzufügen, bevor Sie externen Inhalt laden, sollten Sie in der Lage sein, alle error-Ereignisse zu erfassen, selbst wenn Sie offline laden.

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

Sie können über e.target auf das Element zugreifen, das den Fehler verursacht hat. Wenn Sie beispielsweise wissen möchten, welche Datei in einem img-Tag nicht geladen wurde, können Sie mit e.target.src die URL abrufen, die nicht geladen werden konnte.

HINWEIS: Technisch wird der Fehlercode nicht erkannt. Wenn das Image jedoch nicht geladen werden konnte, verhält es sich unabhängig vom Statuscode technisch gleich. Abhängig von Ihrem Setup wäre dies wahrscheinlich ausreichend, aber wenn zum Beispiel ein 404 mit einem gültigen Image zurückgegeben wird, wird kein Fehlerereignis ausgelöst.

42

Ich habe den folgenden Code in reinem JavaScript zusammengestellt, getestet und es funktioniert. Der gesamte Quellcode (HTML, CSS und Javascript) + Bilder und Beispielschrift ist hier: auf github .

Der erste Codeblock ist ein Objekt mit Methoden für bestimmte Dateierweiterungen: html und css. Der zweite Codeblock wird im Folgenden erklärt. Es folgt jedoch eine kurze Beschreibung.

Es tut Folgendes:

  • die Funktion check_file akzeptiert zwei Argumente: einen String-Pfad und eine Rückruffunktion.
  • ruft den Inhalt des angegebenen Pfads ab
  • ruft die Dateierweiterung (ext) des angegebenen Pfads ab
  • ruft die Objektmethode srcFrom [ext] auf, die ein Array relativer Pfade zurückgibt, auf das im String-Kontext von src, href usw. verwiesen wurde.
  • ruft jeden dieser Pfade im Pfad-Array synchron auf
  • hält bei einem Fehler an und gibt die HTTP-Fehlernachricht und den Pfad zurück, bei dem ein Problem aufgetreten ist, sodass Sie ihn auch für andere Probleme verwenden können, z. B. 403 (verboten) usw.

Der Einfachheit halber werden relative Pfadnamen aufgelöst und es ist nicht wichtig, welches Protokoll verwendet wird (http oder https ist in Ordnung). Bereinigt auch das DOM, nachdem das CSS analysiert wurde.

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;
    }
};

Und hier ist die Funktion, die den Dateiinhalt abruft und die obige Objektmethode entsprechend der Dateierweiterung aufruft:

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);
}

Um es zu benutzen, nenne es einfach so:

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

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

Fühlen Sie sich frei zu kommentieren und zu bearbeiten, wie Sie es wünschen, ich habe dies getan, es ist eine Eile, so dass es nicht so hübsch sein kann :)

4
argon

sie können die Attribute onload und onerror verwenden, um den Fehler zu erkennen

beim Laden der folgenden HTML-Datei wird beispielsweise die Warnung error1 und error2 ausgegeben. Sie können Ihre eigene Funktion aufrufen, z. B. onerror(logError(this);), und diese in einem Array aufzeichnen. Sobald die Seite vollständig geladen ist, ist post mit einem einzigen Ajax-Aufruf.

<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

Sie können XMLHttpRequest für Dateien verwenden.

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();

und verwenden Sie die Image-Klasse für Bilder.

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 gab die Lösung.

Sie können es sogar in vielen Dateien hinzufügen, aber der Fensterhandler kann/sollte einmal hinzugefügt werden.

Ich benutze das Singleton-Muster , um dies zu erreichen:

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;
     };
  }());

Rufen Sie es aus jedem Kontext so oft auf, wie Sie möchten, da der Handler nur angehängt ist einmal :

some_global_object.error();
0
centurian