如何使用JavaScript检测页面404错误?

时间:2011-12-14 12:43:21

标签: javascript html javascript-events http-status-code-404

我有一个HTML页面,其中引用了多个JavaScript,CSS和图像文件。这些引用是动态注入的,用户可以手动将HTML页面和支持文件复制到另一台机器上。

如果缺少某些JS或CSS,浏览器会在控制台中抱怨。例如:

  

错误GET文件:/// E:/SSC_Temp/html_005/temp/Support/jquery.js

我需要以某种方式在HTML页面的内联JavaScript中向我报告这些错误,因此我可以要求用户首先验证是否正确复制了支持文件。

window.onerror事件只是告诉我页面上有JS错误,例如意外语法错误,但是如果发生错误,则不会触发 404 Not Found 错误。我想在任何资源类型的情况下检查这种情况,包括CSS,JS和图像。

我不喜欢使用jQuery AJAX来验证文件是否物理存在 - 每个页面加载的I / O开销都很昂贵。

错误报告必须包含文件名,因此我可以检查该文件是核心文件还是可选文件。

任何想法?

5 个答案:

答案 0 :(得分:44)

要捕获页面上的所有error个事件,您可以addEventListener使用useCapture参数设置为truewindow.onerror不会这样做的原因是因为它使用了气泡事件阶段,而您要捕获的error事件不会冒泡。

如果在加载任何外部内容之前将以下脚本添加到HTML中,则应该能够捕获所有error事件,即使在离线加载时也是如此。

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

您可以通过e.target访问导致错误的元素。例如,如果您想知道img标记上没有加载哪个文件,可以使用e.target.src来获取无法加载的网址。

注意:从技术上讲,它不会检测错误代码,它会检测图像是否无法加载,因为无论状态代码如何,它在技术上都表现相同。根据您的设置,这可能就足够了,但是例如,如果返回带有效图像的404,则不会触发错误事件。

答案 1 :(得分:4)

我将以下代码放在纯JavaScript中,经过测试,并且可以正常运行。 所有源代码(html,css和Javascript)+图像和示例字体都在这里:on github

第一个代码块是一个对象,其中包含特定文件扩展名的方法:htmlcss。 第二个解释如下,但这里有一个简短的描述。

它执行以下操作:

  • 函数check_file有两个参数:一个字符串路径和一个回调函数。
  • 获取给定路径的内容
  • 获取给定路径的文件扩展名(ext
  • 调用srcFrom [ext]对象方法,该方法返回srchref等字符串上下文中引用的相对路径数组。
  • 对路径数组
  • 中的每个路径进行同步调用
  • 暂停错误,并返回HTTP错误消息和有问题的路径,因此您也可以将其用于其他问题,如403(禁止)等。

为方便起见,它解析为相对路径名,并不关心使用哪种协议(http或https,也没关系)。 它还在解析CSS后清理DOM。

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

这是获取文件内容并根据文件扩展名调用上述对象方法的函数:

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

要使用它,只需将其称为:

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

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

请随意评论和编辑,我这样做很匆忙,所以它可能不那么漂亮:)。

答案 2 :(得分:3)

您可以使用 onload onerror 属性来检测错误

例如,在加载以下html后,它会提供警告 error1 error2 ,您可以调用自己的函数,例如onerror(logError(this);)并将其记录在数组中页面是完全加载的帖子是单个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 :(得分:0)

您可以将XMLHttpRequest用于文件。

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

并使用Image类进行图像处理。

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

答案 4 :(得分:0)

@ alexander-omara提供了解决方案。

您甚至可以将其添加到许多文件中,但是可以(应该)添加一次窗口处理程序。

我使用singleton pattern实现了这一点:

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

现在,从任何上下文中调用它都可以,只要一次

some_global_object.error();