Aggiungere interattività con JavaScript

Ilya Grigorik
Ilya Grigorik

JavaScript ci consente di modificare quasi ogni aspetto della pagina: contenuti, stile e la sua risposta all'interazione dell'utente. Tuttavia, JavaScript può anche bloccare la creazione del DOM e ritardare il rendering della pagina. Per ottenere prestazioni ottimali, rendi il tuo codice JavaScript asincrono ed elimina qualsiasi codice JavaScript non necessario dal percorso di rendering critico.

Riepilogo

  • JavaScript può eseguire query e modificare il DOM e il CSSOM.
  • Blocchi di esecuzione JavaScript sul CSSOM.
  • JavaScript blocca la creazione di DOM se non dichiarata esplicitamente come asincrona.

JavaScript è un linguaggio dinamico che viene eseguito in un browser e ci permette di modificare praticamente ogni aspetto del comportamento della pagina: possiamo modificare i contenuti aggiungendo e rimuovendo elementi dall'albero DOM, modificare le proprietà CSSOM di ogni elemento, gestire l'input dell'utente e molto altro ancora. Per spiegare meglio, aggiungiamo al precedente esempio "Hello World" con un semplice script incorporato:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Prova

  • JavaScript ci consente di accedere al DOM ed estrarre il riferimento al nodo intervallo nascosto; il nodo potrebbe non essere visibile nell'albero di rendering, ma è ancora lì nel DOM. Una volta ottenuto il riferimento, possiamo modificarne il testo (tramite .textContent) e persino sostituire la proprietà calcolata dello stile di visualizzazione da "nessuno" a "inline". Ora nella pagina viene visualizzato "Ciao studenti interattivi!".

  • JavaScript consente inoltre di creare, applicare stili, aggiungere e rimuovere nuovi elementi nel DOM. Tecnicamente, l'intera pagina potrebbe essere un solo grande file JavaScript che crea e applica gli stili agli elementi uno alla volta. Sebbene questo metodo funzioni, in pratica l'utilizzo di HTML e CSS è molto più semplice. Nella seconda parte della funzione JavaScript creiamo un nuovo elemento div, impostiamo i contenuti testuali, lo stile e lo aggiungiamo al corpo.

anteprima della pagina

Abbiamo modificato i contenuti e lo stile CSS di un nodo DOM esistente e abbiamo aggiunto un nodo completamente nuovo al documento. La nostra pagina non si aggiudicherà alcun premio per il design, ma dimostra la potenza e la flessibilità che JavaScript ci offre.

Tuttavia, anche se JavaScript ci offre una grande potenza, crea molte altre limitazioni su come e quando viene visualizzata la pagina.

Innanzitutto, nota che nell'esempio sopra riportato il nostro script incorporato si trova in fondo alla pagina. Perché? Dovresti provare anche tu, ma se spostiamo lo script sopra l'elemento span, noterai che lo script ha esito negativo e si lamenta di non riuscire a trovare un riferimento a nessun elemento span nel documento; ovvero, getElementsByTagName("span') restituisce null. Questo dimostra una proprietà importante: lo script viene eseguito nel punto esatto in cui viene inserito nel documento. Quando l'analizzatore sintattico HTML rileva un tag script, mette in pausa il processo di creazione del DOM e fornisce il controllo al motore JavaScript; al termine dell'esecuzione del motore JavaScript, il browser riprende da dove era stato interrotto e riprende la creazione del DOM.

In altre parole, il nostro blocco di script non è in grado di trovare elementi nella pagina successiva perché non sono ancora stati elaborati. Oppure, in modo leggermente diverso: l'esecuzione del nostro script incorporato blocca la costruzione DOM, il che ritarda anche il rendering iniziale.

Un'altra sottile proprietà dell'introduzione degli script nella nostra pagina è che possono leggere e modificare non solo il DOM, ma anche le proprietà CSSOM. Questo è esattamente ciò che stiamo facendo nel nostro esempio, quando modifichiamo la proprietà di visualizzazione dell'elemento span da "nessuna" a "in linea". Il risultato finale? Ora abbiamo una gara.

Cosa succede se il browser non ha completato il download e la creazione del CSSOM quando vogliamo eseguire il nostro script? La risposta è semplice e non ottima per le prestazioni: il browser ritarda l'esecuzione dello script e la creazione del DOM fino al termine del download e della creazione del CSSOM.

In breve, JavaScript introduce molte nuove dipendenze tra il DOM, il CSSOM e l'esecuzione di JavaScript. Di conseguenza, il browser potrebbe subire ritardi significativi nell'elaborazione e nel rendering della pagina sullo schermo:

  • La posizione dello script nel documento è significativa.
  • Quando il browser rileva un tag script, la creazione del DOM si interrompe fino al termine dell'esecuzione dello script.
  • JavaScript può eseguire query e modificare il DOM e il CSSOM.
  • L'esecuzione di JavaScript viene messa in pausa finché il CSSOM non è pronto.

In larga misura, "ottimizzazione del percorso di rendering critico" si riferisce alla comprensione e all'ottimizzazione del grafico delle dipendenze tra HTML, CSS e JavaScript.

Blocco del parser e JavaScript asincrono

Per impostazione predefinita, l'esecuzione di JavaScript è "blocco del parser": quando il browser rileva uno script nel documento, deve sospendere la creazione del DOM, consegnare il controllo al runtime JavaScript e lasciare che lo script venga eseguito prima di procedere con la creazione del DOM. Lo abbiamo visto con uno script incorporato nell'esempio precedente. Infatti, gli script incorporati bloccano sempre l'analizzatore sintattico, a meno che non scriva codice aggiuntivo per rinviarne l'esecuzione.

Che cosa succede agli script inclusi tramite un tag script? Prendiamo l'esempio precedente ed estraiamo il codice in un file separato:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

Prova

Sia che utilizziamo un tag <script> sia uno snippet JavaScript incorporato, ti aspetti che entrambi si comportino allo stesso modo. In entrambi i casi, il browser mette in pausa ed esegue lo script prima di elaborare il resto del documento. Tuttavia, nel caso di un file JavaScript esterno, il browser deve fermarsi per attendere che lo script venga recuperato dal disco, dalla cache o da un server remoto, il che può aggiungere decine a migliaia di millisecondi di ritardo al percorso di rendering critico.

Per impostazione predefinita, tutto il codice JavaScript è bloccato dal parser. Poiché il browser non sa cosa deve fare lo script nella pagina, presume lo scenario peggiore e blocca l'analizzatore sintattico. Un segnale al browser che non è necessario eseguire lo script nel punto esatto in cui viene fatto riferimento consente al browser di continuare a creare il DOM e di eseguire lo script quando è pronto, ad esempio dopo che il file è stato recuperato dalla cache o da un server remoto.

Per ottenere questo risultato, contrassegniamo lo script come async:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Prova

L'aggiunta della parola chiave asincrona al tag script indica al browser di non bloccare la creazione del DOM mentre attende che lo script diventi disponibile, il che può migliorare notevolmente le prestazioni.

Feedback