Как обнаружить ошибки на странице 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 для проверки того, что файл физически существует - накладные расходы ввода-вывода дороги для каждой загрузки страницы.
В отчете об ошибке должно содержаться имя файла, поэтому я могу проверить, является ли файл ядром или необязательным.
Любые идеи?
Ответы
Ответ 1
Чтобы захватить все события 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 возвращается с действительным изображением, это не вызовет событие ошибки.
Ответ 2
Я собрал код ниже в чистом 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 !'); }
});
Пожалуйста, не стесняйтесь комментировать и редактировать, как хотите, я сделал это спешить, так что это может быть не так красиво:)
Ответ 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>
Ответ 4
Вы можете использовать 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();
и используйте класс изображения для изображений.
var img1 = new Image();
img1.src = 'http://yourdomain.net/images/onethatdoesnotexist.gif';
img1.onerror = function () { alert( 'Image not loaded' ); };
Ответ 5
@Александр-Омара дал решение.
Вы даже можете добавить его во многие файлы, но обработчик окна можно/нужно добавить один раз.
Я использую шаблон синглтона для достижения этой цели:
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();