J'ai une page HTML où plusieurs fichiers JavaScript, CSS et images sont référencés. Ces références sont injectées dynamiquement et l'utilisateur peut copier manuellement la page HTML et les fichiers de support sur une autre machine.
Si des fichiers JS ou CSS sont manquants, le navigateur se plaint dans la console. Par exemple:
Fichier d'erreur GET: /// E: /SSC_Temp/html_005/temp/Support/jquery.js
J'ai besoin d'une manière ou d'une autre de ces erreurs qui me sont signalées dans le code JavaScript intégré de la page HTML afin de pouvoir demander à l'utilisateur de vérifier au préalable que les fichiers de support sont copiés correctement.
Il y a l'événement window.onerror
qui vient de m'informer qu'il y a une erreur JS sur la page telle qu'une erreur de syntaxe inattendue, mais qu'elle ne se déclenche pas en cas d'erreur 404 Introuvable . Je souhaite vérifier cette condition dans le cas de tout type de ressource, y compris CSS, JS et images.
Je n'aime pas utiliser jQuery AJAX pour vérifier que le fichier existe physiquement - la surcharge d'E/S est coûteuse pour chaque chargement de page.
Le rapport d'erreur doit contenir le nom du fichier manquant pour que je puisse vérifier si le fichier est core ou facultatif.
Des idées?
Pour capturer tous les événements error
de la page, vous pouvez utiliser addEventListener
avec l'argument useCapture
défini sur true
. La raison pour laquelle window.onerror
ne le fera pas est qu’elle utilise la phase d’événement bulle et que les événements error
que vous souhaitez capturer ne sont pas masqués.
Si vous ajoutez le script suivant à votre code HTML avant de charger du contenu externe, vous devriez pouvoir capturer tous les événements error
, même lors du chargement hors connexion.
<script type="text/javascript">
window.addEventListener('error', function(e) {
console.log(e);
}, true);
</script>
Vous pouvez accéder à l'élément qui a provoqué l'erreur via e.target
. Par exemple, si vous voulez savoir quel fichier n'a pas été chargé sur une balise img
, vous pouvez utiliser e.target.src
pour obtenir l'URL dont le chargement a échoué.
REMARQUE: Techniquement, cela ne détectera pas le code d'erreur, mais si le chargement de l'image échoue, son comportement technique est le même quel que soit le code d'état. En fonction de votre configuration, cela sera probablement suffisant, mais par exemple, si un 404 est renvoyé avec une image valide, il ne déclenchera pas d’événement d’erreur.
J'ai mis en place le code ci-dessous en JavaScript pur, testé et cela fonctionne. Tout le code source (html, css, et Javascript) + images et exemple de police est ici: sur github .
Le premier bloc de code est un objet avec des méthodes pour des extensions de fichier spécifiques: html
et css
. Le second est expliqué ci-dessous, mais voici une brève description.
Il fait ce qui suit:
check_file
prend 2 arguments: un chemin de chaîne et une fonction de rappel.ext
) du chemin donnésrcFrom
[ext
] qui renvoie un tableau de chemins d'accès relatifs référencés dans le contexte de chaîne par src
, href
, etc.Pour des raisons pratiques, il résout les noms de chemin relatifs et ne se soucie pas du protocole utilisé (http ou https, cela ne pose pas de problème). Il nettoie également le DOM après l'analyse du code 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;
}
};
Et voici la fonction qui récupère le contenu du fichier et appelle la méthode d'objet ci-dessus en fonction de l'extension de fichier:
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);
}
Pour l'utiliser, appelez-le simplement comme ceci:
var uri = 'htm/stuff.html'; // html example
check_file(uri, function(err, pth)
{
if (err)
{ document.write('Aw Snap! "'+pth+'" is missing !'); }
});
N'hésitez pas à commenter et éditer à votre guise, je l'ai fait c'est pressé, donc ce n'est peut-être pas si joli :)
vous pouvez utiliser les attributs onload et onerror pour détecter l'erreur
par exemple, lors du chargement du code html suivant, il génère une alerte error1 et error2 vous pouvez appeler votre propre fonction, par exemple onerror(logError(this);)
, et les enregistrer dans un tableau et, une fois la page entièrement chargée, la publication ne comporte qu'un seul appel 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>
Vous pouvez utiliser XMLHttpRequest pour les fichiers.
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();
et utilisez la classe Image pour les images.
var img1 = new Image();
img1.src = 'http://yourdomain.net/images/onethatdoesnotexist.gif';
img1.onerror = function () { alert( 'Image not loaded' ); };
@ alexander-omara a donné la solution.
Vous pouvez même l'ajouter à de nombreux fichiers, mais le gestionnaire de fenêtres peut/devrait être ajouté une fois.
J'utilise le modèle singleton pour y parvenir:
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;
};
}());
Désormais, quel que soit le contexte, appelez-le autant de fois que vous le souhaitez, car le gestionnaire est associé uniquement à une fois:
some_global_object.error();