How to detect on-page 404 errors using JavaScript?

前端 未结 4 1808
借酒劲吻你
借酒劲吻你 2020-12-05 00:30

I have an HTML page where several JavaScript, CSS and images files are referenced. These references are dynamically injected and user can manually copy the HTML page and the

相关标签:
4条回答
  • 2020-12-05 00:48

    I've put together the code below in pure JavaScript, tested, and it works. All the source code (html, css, and Javascript) + images and example font is here: on github.

    The first code block is an object with methods for specific file extensions: html and css. The second is explained below, but here is a short description.

    It does the following:

    • the function check_file takes 2 arguments: a string path and a callback function.
    • gets the contents of given path
    • gets the file extension (ext) of the given path
    • calls the srcFrom [ext] object method that returns an array of relative paths that was referenced in the string context by src, href, etc.
    • makes a synchronous call to each of these paths in the paths array
    • halts on error, and returns the HTTP error message and the path that had a problem, so you can use it for other issues as well, like 403 (forbidden), etc.

    For convenience, it resolves to relative path names and does not care about which protocol is used (http or https, either is fine). It also cleans up the DOM after parsing the 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;
        }
    };
    

    And here is the function that gets the file contents and calls the above object method according to the file extension:

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

    To use it, simply call it like this:

    var uri = 'htm/stuff.html';    // html example
    
    check_file(uri, function(err, pth)
    {
        if (err)
        { document.write('Aw Snap! "'+pth+'" is missing !'); }
    });
    

    Please feel free to comment and edit as you wish, i did this is a hurry, so it may not be so pretty :)

    0 讨论(0)
  • 2020-12-05 00:55

    To capture all error events on the page, you can use addEventListener with the useCapture argument set to true. The reason window.onerror will not do this is because it uses the bubble event phase, and the error events you want to capture do not bubble.

    If you add the following script to your HTML before you load any external content, you should be able to capture all the error events, even when loading offline.

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

    You can access the element that caused the error through e.target. For example, if you want to know what file did not load on an img tag, you can use e.target.src to get the URL that failed to load.

    NOTE: This technically will not detect the error code, it detects if the image failed to load, as it technically behaves the same regardless of the status code. Depending on your setup this would probably be enough, but for example if a 404 is returned with a valid image it will not trigger an error event.

    0 讨论(0)
  • 2020-12-05 01:05

    @alexander-omara gave the solution.

    You can even add it in many files but the window handler can/should be added once.

    I use the singleton pattern to achieve this:

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

    Now, from any context call it as many times you want as the handler is attached only once:

    some_global_object.error();
    
    0 讨论(0)
  • 2020-12-05 01:06

    you can use the onload and onerror attributes to detect the error

    for example upon loading the following html it gives alert error1 and error2 you can call your own function e.g onerror(logError(this);) and record them in an Array and once the page is fully loaded post is with single Ajax call.

    <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>
    
    0 讨论(0)
提交回复
热议问题