Il browser permette di tracciare il caricamento di risorse esterne: script, iframe, immagini e così via.
Esistono 2 eventi per tracciare il caricamento:
onload– caricato con successo,onerror– si è verificato un errore.
Caricamento di uno script
Diciamo che abbiamo necessità di caricare uno script di terze parti e chiamare una funzione che appartiene a questo script.
Possiamo caricarlo dinamicamente, in questo modo:
let script = document.createElement('script');
script.src="/?originalUrl=https%3A%2F%2Fit.javascript.info%2F%26quot%3Bmy.js%26quot%3B%3Bdocument.head.append(script)%3B%253C%2Fcode">
…Ma come possiamo eseguire la funzione dichiarata all’interno di quello script? Dobbiamo attendere la fine del caricamento dello script e successivamente chiamare la funzione.
Per i nostri script dovremmo utilizzare i moduli JavaScript in questo caso, ma non sono largamente adottati dalle librerie di terze parti.
script.onload
Il principale helper è l’evento load. Si innesca dopo che lo script è stato caricato ed eseguito.
Per esempio:
let script = document.createElement('script');
// si può caricare qualunque script, da qualunque dominio
script.src="/?originalUrl=https%3A%2F%2Fit.javascript.info%2F%26quot%3Bhttps%3A%2F%2Fcdnjs.cloudflare.com%2Fajax%2Flibs%2Flodash.js%2F4.3.0%2Flodash.js%26quot%3Bdocument.head.append(script)%3Bscript.onload%2520%3D%2520function()%2520%257B%2520%2520%2F%2F%2520lo%2520script%2520crea%2520una%2520funzione%2520helper%2520%26quot%3B_%26quot%3B%2520%2520alert(_)%3B%2520%2F%2F%2520la%2520funzione%2520%25C3%25A8%2520disponibile%257D%3B%253C%2Fcode">
Quindi nell’evento onload possiamo utilizzare le variabili dello script, eseguire funzioni, ecc.
script.onerror
Gli errori che si verificano durante il caricamento dello script possono essere tracciati tramite l’evento error.
! script.onerror = function() { alert("Caricamento fallito " + this.src); // Error loading https://example.com/404.js }; /!
Notate bene che in questo punto non possiamo ottenere i dettagli dell'errore HTTP. Non sappiamo se è un errore 404 o 500 o qualcos'altro.
```warn
Gli eventi `onload`/`onerror` tracciano solo il caricamento stesso.
Gli eventi load e error funzionano anche per le altre risorse, praticamente per qualunque risorsa che ha un src esterno.
Per esempio:
let img = document.createElement('img');
img.src="/?originalUrl=https%3A%2F%2Fit.javascript.info%2F%26quot%3Bhttps%3A%2F%2Fjs.cx%2Fclipart%2Ftrain.gif%26quot%3B%3B%2520%2F%2F%2520(*)img.onload%2520%3D%2520function()%2520%257B%2520%2520alert(%2560Immagine%2520caricata%2C%2520dimensione%2520%24%257Bimg.width%257Dx%24%257Bimg.height%257D%2560)%3B%257D%3Bimg.onerror%2520%3D%2520function()%2520%257B%2520%2520alert(%26quot%3BSi%2520%25C3%25A8%2520verificato%2520un%2520errore%2520durante%2520il%2520caricamento%2520dell"immagine");
};
Ci sono alcune note però:
- Per gli
<iframe>, l’eventoiframe.onloadsi aziona quando il caricamento dell’ iframe è terminato, sia in caso di successo che in caso di errore.
C’è una regola: gli script di un sito non possono accedere ai contenuti di un altro sito. Quindi, per esempio, uno script di https://facebook.com non può leggere la casella di posta dell’utente di https://gmail.com.
Per essere più precisi, un’origine (tripletta dominio/porta/protocollo) non può accedere al contenuto di un’altra. Quindi se abbiamo un sottodominio, o anche solo un’altra porta, questo sarà un’origine differente e quindi non hanno accesso l’uno con l’altro.
Questa regola interessa anche le risorse di altri domini.
Se stiamo utilizzando uno script di un altro dominio e c’è un errore, non possiamo ottenere i dettagli di quell’errore.
Per esempio, prendiamo lo script error.js, che consiste in una singola chiamata ad una funzione (sbagliata):
// 📁 error.js
noSuchFunction();
Ora caricatela dallo stesso sito su cui è situato lo script:
<script>
window.onerror = function(message, url, line, col, errorObj) {
alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="/?originalUrl=https%3A%2F%2Fit.javascript.info%2F%26quot%3B%2Farticle%2Fonload-onerror%2Fcrossorigin%2Ferror.js%26quot%3B%26gt%3B%26lt%3B%2Fscript%26gt%3B%253C%2Fcode">
Vedremo il report dell’errore, come questo:
Uncaught ReferenceError: noSuchFunction is not defined
https://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1
Ora carichiamo lo stesso script da un altro dominio:
<script>
window.onerror = function(message, url, line, col, errorObj) {
alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="/?originalUrl=https%3A%2F%2Fit.javascript.info%2F%26quot%3Bhttps%3A%2F%2Fcors.javascript.info%2Farticle%2Fonload-onerror%2Fcrossorigin%2Ferror.js%26quot%3B%26gt%3B%26lt%3B%2Fscript%26gt%3B%253C%2Fcode">
Il report di errore è diverso rispetto a quello precedente, come questo:
Script error.
, 0:0
Ci sono molti servizi (e possiamo anche sviluppare il nostro) che stanno in ascolto sugli errori globali, utilizzando window.onerror, salvano gli errori e forniscono un interfaccia per accedere ed analizzarli. Fantastico, possiamo vedere i veri errori, scaturiti dai nostri utenti. Ma se uno script è caricato da un altro dominio non avremo nessuna informazioni sull’errore, come abbiamo appena visto.
Una policy cross-origin (CORS) simile viene applicata anche per altri tipi di risorse.
Per consentire l’accesso cross-origin il tag <script> deve avere l’attributo crossorigin e il server remoto deve fornire degli header speciali.
Ci sono tre livelli di accesso cross-origin:
- Attributo
crossoriginnon presente – accesso vietato. crossorigin="anonymous"– accesso consentito se il server risponde con l’headerAccess-Control-Allow-Origincon il valore*o il nome della nostra origin (dominio). Il browser non manda dati e cookie sull’autenticazione al server remoto.crossorigin="use-credentials"– accesso consentito se il server manda indietro l’headerAccess-Control-Allow-Origincon la nostra origine (dominio) eAccess-Control-Allow-Credentials: true. Il browser manda i dati e i cookie sull’autenticazione al server remoto.
Puoi approfondire l’accesso cross-origin nel capitolo Fetch: Cross-Origin Requests. Descrive il metodo fetch per le richieste di rete, ma la policy è esattamente la stessa.
Ad esempio i “cookies” sono un argomento fuori dal nostro attuale ambito, ma puoi leggere informazioni a proposito nel capitolo Cookies, document.cookie.
Nel nostro caso non avevamo nessun attributo crossorigin, quindi l’accesso era vietato. Aggiungiamo l’attributo ora.
Possiamo scegliere tra "anonymous" (non vengono mandati cookie, è necessario un header lato server) e "use-credentials" (manda i cookie, sono necessari 2 header lato server).
Se non ci interessano i cookie allora "anonymous" è la scelta giusta:
<script>
window.onerror = function(message, url, line, col, errorObj) {
alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script crossorigin="anonymous" src="/?originalUrl=https%3A%2F%2Fit.javascript.info%2F%26quot%3Bhttps%3A%2F%2Fcors.javascript.info%2Farticle%2Fonload-onerror%2Fcrossorigin%2Ferror.js%26quot%3B%26gt%3B%26lt%3B%2Fscript%26gt%3B%253C%2Fcode">
Ora, supponendo che il server fornisca l’header Access-Control-Allow-Origin, riusciamo ad avere il report completo dell’errore.
Riepilogo
Immagini <img>, fogli di stile esterni, script e altre risorse forniscono gli eventi load e error per tracciare i loro caricamento:
loadsi aziona se il caricamento va a buon fine,errorsi azione se si verifica un errore durante il caricamento.
L’unica eccezione è <iframe>: per ragioni storiche scatta sempre l’evento load, per qualunque esito del caricamento, anche se la pagina non è stata trovata.
Possiamo monitorare il caricamento delle risorse anche tramite l’evento readystatechange, ma è poco utilizzato, perché gli eventi load/error sono più semplici.
Commenti
<code>, per molte righe – includile nel tag<pre>, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)