web-dev-qa-db-ja.com

JavaScriptを使用してページ404エラーを検出する方法

いくつかのJavaScript、CSS、および画像ファイルが参照されているHTMLページがあります。これらの参照は動的に挿入され、ユーザーはHTMLページとサポートファイルを別のマシンに手動でコピーできます。

一部のJSまたはCSSが欠落している場合、ブラウザーはコンソールで文句を言います。例えば:

エラーGET file:/// E:/SSC_Temp/html_005/temp/Support/jquery.js

サポートファイルが正しくコピーされていることを最初に確認するようにユーザーに要求できるように、HTMLページのインラインJavaScriptでこれらのエラーを何らかの方法で報告する必要があります。

window.onerror予期しない構文エラーなどのJSエラーがページにあることを通知するだけのイベントですが、404が見つからない場合は発生しませんエラー。 CSS、JS、画像などのリソースタイプの場合に、この状態を確認したいと思います。

JQuery AJAX=を使用してファイルが物理的に存在することを確認するのは好きではありません。I/ Oオーバーヘッドは、ページの読み込みごとに高額です。

エラーレポートにはファイル名が含まれている必要があるため、ファイルがコアかオプションかを確認できます。

何か案は?

27
Santoo

ページ上のすべてのerrorイベントをキャプチャするには、_ addEventListener を使用し、useCapture引数をtrueに設定します。 window.onerrorがこれを行わない理由は、バブルイベントフェーズを使用し、キャプチャするerrorイベントがバブルしないためです。

外部コンテンツを読み込む前に次のスクリプトをHTMLに追加すると、オフラインで読み込んでも、すべてのerrorイベントをキャプチャできるはずです。

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

エラーの原因となった要素にはe.targetからアクセスできます。たとえば、imgタグに読み込まれなかったファイルを知りたい場合は、e.target.srcを使用して、読み込みに失敗したURLを取得できます。

注:技術的にはステータスコードに関係なく同じように動作するため、これは技術的にはエラーコードを検出せず、画像の読み込みに失敗したかどうかを検出します。設定によってはこれで十分でしょう。たとえば、有効な画像とともに404が返された場合、エラーイベントはトリガーされません。

49

以下のコードを純粋なJavaScriptにまとめてテストしましたが、うまくいきました。すべてのソースコード(html、css、およびJavaScript)+画像とフォントの例は、こちら githubで です。

最初のコードブロックは、特定のファイル拡張子(htmlおよびcss)のメソッドを持つオブジェクトです。 2つ目については以下で説明しますが、簡単な説明を次に示します。

次のことを行います。

  • 関数 check_fileは2つの引数を取ります。文字列パスとコールバック関数です。
  • 指定されたパスの内容を取得します
  • 指定されたパスのファイル拡張子(ext)を取得します
  • srcFromextなどによって文字列コンテキストで参照された相対パスの配列を返すsrc [href]オブジェクトメソッドを呼び出します。
  • パス配列のこれらの各パスに対して同期呼び出しを行います
  • エラーで停止し、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 !'); }
});

自由にコメントして編集してください。これは急いで行ったので、それほどきれいではないかもしれません:)

4
user4244405

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
Zeronex

ファイルには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' ); };
0
Ruslan López

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

これで、任意のコンテキストから、ハンドラーがonceのみにアタッチされるので、必要な回数だけ呼び出します。

some_global_object.error();
0
centurian