Ajouter de l'interactivité avec JavaScript

Ilya Grigorik
Ilya Grigorik

JavaScript permet de modifier à peu près tous les aspects d'une page: son contenu, son style et sa réponse aux interactions des utilisateurs. Toutefois, JavaScript peut également bloquer la construction DOM et retarder l'affichage de la page. Pour obtenir des performances optimales, rendez votre code JavaScript asynchrone et éliminez tout code JavaScript inutile du chemin critique du rendu.

Résumé

  • JavaScript peut interroger et modifier le DOM et le CSSOM.
  • Blocs d'exécution JavaScript sur CSSOM.
  • JavaScript bloque la construction DOM, sauf si elle est explicitement déclarée comme asynchrone.

JavaScript est un langage dynamique qui s'exécute dans un navigateur et nous permet de modifier à peu près tous les aspects du comportement de la page: nous pouvons modifier le contenu en ajoutant et en supprimant des éléments de l'arborescence DOM, modifier les propriétés CSSOM de chaque élément, gérer les entrées utilisateur et bien plus encore. Pour illustrer cela, ajoutons un simple script intégré à notre exemple "Hello World" précédent:

<!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>

Essayer

  • JavaScript nous permet d'accéder au DOM et d'extraire la référence au nœud de segment caché. Il est possible que le nœud ne soit pas visible dans l'arborescence de rendu, mais qu'il se trouve toujours dans le DOM. Ensuite, lorsque nous disposons de la référence, nous pouvons modifier son texte (via .textContent), voire remplacer la propriété de style d'affichage calculée de "none" par "inline". Notre page affiche maintenant le message "Hello interactive classrooms!" (Bonjour élèves interactifs).

  • JavaScript permet également de créer, d'appliquer un style, d'ajouter et de supprimer de nouveaux éléments dans le DOM. Techniquement, notre page entière pourrait n'être qu'un seul gros fichier JavaScript qui crée et stylise les éléments un par un. Bien que cela fonctionne, dans la pratique, il est beaucoup plus facile d'utiliser les langages HTML et CSS. Dans la deuxième partie de notre fonction JavaScript, nous allons créer un élément div, définir son contenu textuel, lui appliquer un style et l'ajouter au corps.

aperçu de la page

Nous avons ainsi modifié le contenu et le style CSS d'un nœud DOM existant, et ajouté un tout nouveau nœud au document. Notre page ne remportera aucun prix de conception, mais elle illustre la puissance et la flexibilité que JavaScript nous offre.

Toutefois, bien que JavaScript offre une grande puissance, il crée de nombreuses restrictions supplémentaires concernant le mode et le moment d'affichage des pages.

Tout d'abord, notez que dans l'exemple ci-dessus, notre script intégré se trouve vers le bas de la page. Pourquoi ? Vous devriez essayer vous-même. Toutefois, si nous déplaçons le script au-dessus de l'élément span, vous remarquerez qu'il échoue et se plaint de ne trouver aucune référence à un élément span du document. Autrement dit, getElementsByTagName('span') renvoie la valeur null. Cela démontre une propriété importante: notre script est exécuté au point exact où il est inséré dans le document. Lorsque l'analyseur HTML rencontre un tag de script, il interrompt son processus de construction du DOM et cède le contrôle au moteur JavaScript. Une fois l'exécution du moteur JavaScript terminée, le navigateur reprend là où il s'était arrêté et reprend la construction du DOM.

En d'autres termes, notre bloc de script ne trouve aucun élément plus tard sur la page, car ils n'ont pas encore été traités. Autrement dit, l'exécution de notre script intégré bloque la construction DOM, ce qui retarde également l'affichage initial.

Une autre subtilité de l'introduction des scripts sur notre page est qu'ils peuvent lire et modifier non seulement le DOM, mais aussi les propriétés CSSOM. En fait, c'est exactement ce que nous faisons dans notre exemple, lorsque nous modifions la propriété d'affichage de l'élément "span" de "none" à "inline". Quelles conséquences cela a-t-il ? Nous avons maintenant une condition de concurrence.

Que se passe-t-il si le navigateur n'a pas fini de télécharger et de créer le CSSOM lorsque nous voulons exécuter notre script ? La réponse est simple et pas très performante: le navigateur retarde l'exécution du script et la construction du DOM jusqu'à ce que le téléchargement et la construction du CSSOM soient terminés.

En résumé, JavaScript introduit de nombreuses nouvelles dépendances entre le DOM, le CSSOM et l'exécution JavaScript. Par conséquent, le navigateur peut retarder considérablement le traitement et l'affichage de la page à l'écran:

  • L'emplacement du script dans le document est important.
  • Lorsque le navigateur rencontre un tag de script, la construction DOM est interrompue jusqu'à la fin de l'exécution du script.
  • JavaScript peut interroger et modifier le DOM et le CSSOM.
  • L'exécution de JavaScript s'interrompt jusqu'à ce que le CSSOM soit prêt.

Dans une grande mesure, "optimiser le chemin critique du rendu" désigne la compréhension et l'optimisation du graphique de dépendance entre HTML, CSS et JavaScript.

Blocage de l'analyseur et JavaScript asynchrone

Par défaut, l'exécution JavaScript est bloquée par l'analyseur : lorsque le navigateur rencontre un script dans le document, il doit interrompre la construction du DOM, laisser le contrôle à l'environnement d'exécution JavaScript et laisser le script s'exécuter avant de poursuivre la construction DOM. Nous avons vu cela en action avec un script intégré dans notre exemple précédent. En fait, les scripts intégrés bloquent toujours l'analyseur, sauf si vous écrivez du code supplémentaire pour différer leur exécution.

Qu'en est-il des scripts inclus via un tag de script ? Prenons l'exemple précédent et extrayons le code dans un fichier distinct:

<!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);

Essayer

Avec une balise <script> ou un extrait de code JavaScript intégré, vous devez vous attendre à ce que les deux se comportent de la même manière. Dans les deux cas, le navigateur interrompt et exécute le script avant de pouvoir traiter le reste du document. Toutefois, dans le cas d'un fichier JavaScript externe, le navigateur doit attendre que le script soit récupéré sur le disque, le cache ou un serveur distant, ce qui peut ajouter des dizaines, voire des milliers de millisecondes de retard au chemin de rendu critique.

Par défaut, tout JavaScript est bloquant par l'analyseur. Comme le navigateur ne sait pas ce que le script prévoit de faire sur la page, il suppose le pire des scénarios et bloque l'analyseur. Si vous indiquez au navigateur que le script n'a pas besoin d'être exécuté au point exact où il est référencé, le navigateur peut continuer à construire le DOM et laisser le script s'exécuter lorsqu'il est prêt, par exemple après la récupération du fichier du cache ou d'un serveur distant.

Pour ce faire, nous marquerons notre script comme 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>

Essayer

L'ajout du mot clé asynchrone au tag de script indique au navigateur de ne pas bloquer la construction DOM en attendant que le script soit disponible. Cela peut améliorer considérablement les performances.

Commentaires