it-swarm.com.ru

Как обнаружить на странице 404 ошибки с помощью JavaScript?

У меня есть страница HTML, где есть ссылки на несколько файлов JavaScript, CSS и изображений. Эти ссылки вводятся динамически, и пользователь может вручную скопировать HTML-страницу и файлы поддержки на другой компьютер.

Если некоторые JS или CSS отсутствуют, браузер жалуется в консоли. Например:

Файл GET ошибки: /// E: /SSC_Temp/html_005/temp/Support/jquery.js

Мне нужно как-то сообщить об этих ошибках мне на встроенном JavaScript HTML-страницы, чтобы я мог попросить пользователя сначала проверить, правильно ли скопированы файлы поддержки.

Есть событие window.onerror, которое просто сообщает мне, что на странице есть ошибка JS, такая как непредвиденная синтаксическая ошибка, но она не срабатывает в случае ошибки 404 Not Found. Я хочу проверить это условие в случае любого типа ресурса, включая CSS, JS и изображения.

Я не люблю использовать jQuery AJAX для проверки того, что файл физически существует - накладные расходы ввода-вывода являются дорогостоящими для каждой загрузки страницы. 

Отчет об ошибке должен содержать имя файла missing, чтобы я мог проверить, является ли файл основным или необязательным.

Есть идеи?

19
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 возвращается с действительным изображением, это не вызовет событие ошибки.

42
Alexander O'Mara

Я собрал приведенный ниже код в чистом JavaScript, протестировал, и он работает . Весь исходный код (html, css и Javascript) + картинки и пример шрифта здесь: на github .

Первый блок кода представляет собой объект с методами для определенных расширений файлов: html и css. Второй поясняется ниже, но здесь приводится краткое описание.

Это делает следующее:

  • функция check_file принимает 2 аргумента: строковый путь и функцию обратного вызова.
  • получает содержимое указанного пути
  • получает расширение файла (ext) по указанному пути
  • вызывает метод объекта srcFrom [ext], который возвращает массив относительных путей, на которые ссылались в строковом контексте с помощью src, href и т. д.
  • выполняет синхронный вызов для каждого из этих путей в массиве путей
  • останавливается при ошибке и возвращает сообщение об ошибке HTTP и путь, по которому возникла проблема, так что вы можете использовать его и для других проблем, таких как 403 (запрещено) и т. д.

Для удобства он разрешает относительные имена путей и не заботится о том, какой протокол используется (http или https, либо в порядке) . Он также очищает DOM после анализа 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;
    }
};

А вот функция, которая получает содержимое файла и вызывает вышеуказанный метод объекта в соответствии с расширением файла:

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
argon

вы можете использовать атрибуты 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

@ Александр-Омара дал решение.

Вы даже можете добавить его во многие файлы, но обработчик окна можно/нужно добавить один раз.

Я использую шаблон синглтона для достижения этой цели:

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