Načítání zdrojů:onload a onerror

Prohlížeč nám umožňuje sledovat načítání externích zdrojů – skriptů, prvků iframe, obrázků a tak dále.

Existují dvě události:

  • onload – úspěšné načtení,
  • onerror – došlo k chybě.

Načítání skriptu

Řekněme, že potřebujeme načíst skript třetí strany a zavolat funkci, která se tam nachází.

Můžeme jej načíst dynamicky, takto:

let script = document.createElement('script');
script.src = "my.js";

document.head.append(script);

…Ale jak spustit funkci, která je deklarována uvnitř tohoto skriptu? Musíme počkat, až se skript načte, a teprve potom jej můžeme zavolat.

Poznámka:

Pro naše vlastní skripty bychom zde mohli použít moduly JavaScriptu, ale knihovny třetích stran je příliš nepřejímají.

script.onload

Hlavním pomocníkem je load událost. Spustí se po načtení a spuštění skriptu.

Například:

let script = document.createElement('script');

// can load any script, from any domain
script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"
document.head.append(script);

script.onload = function() {
 // the script creates a variable "_"
 alert( _.VERSION ); // shows library version
};

Tedy v onload můžeme použít skriptové proměnné, spouštět funkce atd.

…A co když se načítání nezdařilo? Například neexistuje žádný takový skript (chyba 404) nebo je server mimo provoz (nedostupný).

script.onerror

Chyby, ke kterým dojde během načítání skriptu, lze sledovat v error událost.

Požádejme například o skript, který neexistuje:

let script = document.createElement('script');
script.src = "https://example.com/404.js"; // no such script
document.head.append(script);

script.onerror = function() {
 alert("Error loading " + this.src); // Error loading https://example.com/404.js
};

Upozorňujeme, že zde nemůžeme získat podrobnosti o chybě HTTP. Nevíme, jestli to byla chyba 404 nebo 500 nebo něco jiného. Právě že načítání se nezdařilo.

Důležité:

Události onload /onerror sledovat pouze samotné načítání.

Chyby, ke kterým může dojít během zpracování a provádění skriptu, jsou mimo rozsah těchto událostí. To znamená:pokud se skript úspěšně načetl, pak onload spouští, i když má programátorské chyby. Chcete-li sledovat chyby skriptu, můžete použít window.onerror globální ovladač.

Další zdroje

load a error události fungují také pro jiné zdroje, v podstatě pro jakýkoli zdroj, který má externí src .

Například:

let img = document.createElement('img');
img.src = "https://js.cx/clipart/train.gif"; // (*)

img.onload = function() {
 alert(`Image loaded, size ${img.width}x${img.height}`);
};

img.onerror = function() {
 alert("Error occurred while loading image");
};

Přesto jsou zde některé poznámky:

  • Většina zdrojů se začne načítat, když jsou přidány do dokumentu. Ale <img> je výjimkou. Začne se načítat, když dostane src (*) .
  • Pro <iframe> , iframe.onload událost se spustí po dokončení načítání prvku iframe, a to jak pro úspěšné načtení, tak pro případ chyby.

Je to z historických důvodů.

Zásady crossorigin

Existuje pravidlo:skripty z jednoho webu nemají přístup k obsahu druhého webu. Takže např. skript na https://facebook.com nemůže číst poštovní schránku uživatele na https://gmail.com .

Nebo, abychom byli přesnější, jeden původ (triplet doména/port/protokol) nemá přístup k obsahu z jiného. Takže i když máme subdoménu nebo jen jiný port, jedná se o různé zdroje bez přístupu k sobě navzájem.

Toto pravidlo také ovlivňuje zdroje z jiných domén.

Pokud používáme skript z jiné domény a je v něm chyba, nemůžeme získat podrobnosti o chybě.

Vezměme si například skript error.js který se skládá z jediného (špatného) volání funkce:

// 📁 error.js
noSuchFunction();

Nyní jej načtěte ze stejného webu, kde se nachází:

<script>
window.onerror = function(message, url, line, col, errorObj) {
 alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="/article/onload-onerror/crossorigin/error.js"></script>

Můžeme vidět dobré chybové hlášení, jako je toto:

Uncaught ReferenceError: noSuchFunction is not defined
https://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1

Nyní načteme stejný skript z jiné domény:

<script>
window.onerror = function(message, url, line, col, errorObj) {
 alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"></script>

Přehled je jiný, například takto:

Script error.
, 0:0

Podrobnosti se mohou lišit v závislosti na prohlížeči, ale myšlenka je stejná:veškeré informace o vnitřnostech skriptu, včetně trasování zásobníku chyb, jsou skryté. Přesně proto, že je z jiné domény.

Proč potřebujeme podrobnosti o chybě?

Existuje mnoho služeb (a my si můžeme vytvořit vlastní), které naslouchají globálním chybám pomocí window.onerror , ukládat chyby a poskytovat rozhraní pro přístup k nim a jejich analýzu. To je skvělé, protože vidíme skutečné chyby vyvolané našimi uživateli. Ale pokud skript pochází z jiného původu, pak v něm není mnoho informací o chybách, jak jsme právě viděli.

Podobná cross-origin policy (CORS) je vynucována i pro jiné typy zdrojů.

Chcete-li umožnit přístup mezi zdroji, <script> značka musí mít crossorigin a vzdálený server musí poskytnout speciální hlavičky.

Existují tři úrovně přístupu mezi zdroji:

  1. Ne crossorigin atribut – přístup zakázán.
  2. crossorigin="anonymous" – přístup povolen, pokud server odpoví hlavičkou Access-Control-Allow-Origin s * nebo náš původ. Prohlížeč neposílá autorizační informace a soubory cookie vzdálenému serveru.
  3. crossorigin="use-credentials" – přístup povolen, pokud server odešle zpět hlavičku Access-Control-Allow-Origin s naším původem a Access-Control-Allow-Credentials: true . Prohlížeč odešle informace o autorizaci a soubory cookie na vzdálený server.
Poznámka:

Více o přístupu napříč původem si můžete přečíst v kapitole Načítání:Požadavky na křížový původ. Popisuje fetch metoda pro síťové požadavky, ale politika je úplně stejná.

Něco jako „cookies“ je mimo náš současný rozsah, ale můžete si o nich přečíst v kapitole Cookies, document.cookie.

V našem případě jsme neměli žádný atribut crossorigin. Proto byl přístup napříč původem zakázán. Pojďme to přidat.

Můžeme si vybrat mezi "anonymous" (žádné soubory cookie nebyly odeslány, je potřeba jedna hlavička na straně serveru) a "use-credentials" (také odesílá soubory cookie, jsou potřeba dvě hlavičky na straně serveru).

Pokud nás nezajímají soubory cookie, pak "anonymous" je správná cesta:

<script>
window.onerror = function(message, url, line, col, errorObj) {
 alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script crossorigin="anonymous" src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"></script>

Nyní za předpokladu, že server poskytuje Access-Control-Allow-Origin záhlaví, vše v pořádku. Máme úplnou zprávu o chybě.

Shrnutí

Obrázky <img> , externí styly, skripty a další zdroje poskytují load a error události ke sledování jejich načítání:

  • load spouští při úspěšném načtení,
  • error spouští při neúspěšném načtení.

Jedinou výjimkou je <iframe> :z historických důvodů vždy spouští load , pro jakékoli dokončení načítání, i když stránka nebyla nalezena.

readystatechange událost také funguje pro zdroje, ale používá se zřídka, protože load/error události jsou jednodušší.