HTML5 et API web : les quirks sans quirks‑mode

Auteur Message
Un sujet qui répertorie les petites choses qui peuvent surprendre ou laisser dans l’incompréhension, avec des explications. Ce sera un sujet pour les problèmes, il faudrait un sujet à part pour les choses qui se discutent sans être des problèmes même si elles peuvent humainement en devenir, ce qui est le cas avec les questions d’organisation et d’ergonomie.

Quand on ajoute des contrôles numériques à un formulaire, on peut définir les valeurs `min` et `max` et `step` dans le HTML ou alors les laisser indéfinies et les définir depuis JavaScript quand la page est complètement lue.

Dans le premier cas (quand on défini les valeurs dans le HTML), le navigateur choisi des dimensions appropriées pour le contrôle, par exemple petit si l’intervalle va de zéro à 1 et grand si l’intervalle est grand, la taille du contrôle varie (constaté au moins avec Firefox et Webkit). Tandis que dans le second cas (quand on défini les valeurs depuis JavaScript seulement), le navigateur choisi des dimensions par défaut et ce sont toujours les même, c’est homogène. Dans ce second cas, il faut penser à définir la largeur adaptée, soit dans le style CSS, soit depuis JavaScript.
Certaines API sont restreintes aux contextes sécurisés (les secure context). Normalement, ce qui est local, est considéré comme sécure, parce qu’il n’y a pas de communication passant par l’extérieure. Mais parfois le navigateur peut encore être restrictif même quand il confirme que le contexte est sécure. Ces détails sont potentiellement temporel, parce qu’on peut les considérer comme des bugs, étant donné qu’on peut légitimement dire qu’un contexte est sécure, même si le navigateur ne le considère pas comme tel. Il se peut donc que ces commentaires ne soient plus pertinents dans quelques années.

Il y a le cas de Firefox qui refuse l’accès au MIDI depuis une page HTML enregistrée localement et ouverte comme un fichier. Depuis la console JavaScript on peut avoir la confirmation que le contexte est sécure, mais l’interface MIDI n’est pas disponible (elle est undefined). Ça peut être considéré comme un bug du navigateur.

Quand la page est fournie par un serveur web locale, il peut aussi y avoir des conditions qui ne devrait pas être exigée. Si le nom de domaine est localhost, tout va bien. Si c’est un sous‑domaine de localhost, alors ça peut marcher ou pas. Surf par exemple (un navigateur basé sur WebKit), ira toujours sur le domaine principale localhost, si on lui demande d’aller sur un sous‑domaine, comme par exemple site2.localhost (en attente d’investigation). Ça fonctionne cependant bien avec Firefox, ce qui conforme bien qu’il y a un quelque chose qui cloche.

Si le nom de domaine local a été personnalisé, par exemple un site qui s’appelle mon-site.ici avec une entrée dans le fichier hosts (qu’on peut avoir avec Windows, même si ça se sait moins) qui indique bien que mon-site.ici est à l’adresse 127.0.0.1, au moins avec Firefox, le site n’est pas considéré comme sécure si on y accède pas en HTTPS ; il est considéré comme site extérieur, pas local, à cause de ce qu’il n’est pas un domaine de localhost, et ceci, même si son IP est 127.0.0.1
Apparemment, Event.target peut être null.
La syntax des types avec JSDoc, inclus aussi celle de Google Closure. Pour faire un contrôle static des types, il ne semble pas exister d’autres solution que TypeScript, qui n’est pas parfait (des exemples de bugs seront décrits plus tard).

Il faut donc garder des liens vers ces références :

  • Types in the Closure Type System (github.com). Seulement pour les expressions de types, pas pour les autres tags, qui peuvent être semblable ou différents ; ne pas confondre les deux. Pour que toutes ces expressions soient reconnues par JSDoc, il faut en utiliser une versions assez récente. Si vous n’utilisez pas JSDoc directement, mais seulement sa syntaxe avec ESLint et/ou TypeScript, il faut vérifier quelle version de la syntaxe de JSDoc est supportée … elle est généralement assez récente, à condition d’utiliser un ESLint et un TypeScript pas trop ancien.
  • Generic Types (github.com), avec les mêmes remarques que plus haut.
  • JSDoc Reference (typescriptlang.org), qui est pertinent si vous utiliser TypeScript pour vérifier le typage indiqué par les commentaires JSDoc (sinon ces commentaires servent à documenter, mais en rassurent pas plus).

Note : en mentionnant TypeScript, il s’agit de l’utiliser pour vérifier les types, pas pour compiler. Bien que TypeScript compile un langage proche de JavaScript, vers JavaScript, il peut aussi vérifier le bon typage d’un source JavaScript, directement, avec par exemple la commande ci‑dessous :

Code : 

tsc \
--noEmit \
--allowJs \
--target es2023 \
--checkJs \
--strict \
--strictNullChecks \
$FILE

… où $FILE est le nom du fichier source JavaScript. Le numéro de version d’ECMAScript (le vrai nom formel de JavaScript), devra être changé dans le future pas trop lointain. Laversion 2023, est celle la plus en cours, même s’il existe des versions plus récentes. L’exemple donné utilise des échappement pour les sauts de ligne, parce que si la commande était écrire en une seule ligne, elle serait trop longue et pas entièrement lisible.
Si un site s’affiche normalement sur un écran d’ordi’ mais s’affiche comme en zoom réduit, en caractères minuscules sur un smartphone, alors qu’il n’y a pas d’éléments trop grands dans la page, une solution probable est d’ajouter cet élément dans l’entête du HTML :

Code : 

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Avec un site simple ou des pages pas trop denses, c’est suffisant, rien d’autre à faire. C’est tellement simple que ça pourrait être automatique, mais ça ne l’est pas. les navigateurs pourraient choisir par exemple une taille de texte adapté à l’écran, tout simplement, mais au lieu de ça, il considère par défaut que le site est pour un ordi’ et que pour l’afficher sur un smartphone, il faut faire un zoom en réduction. Avec cet élément, on dit que non, qu’il faut simplement que le site s’affiche comme il est, avec la taille d’écran disponible, sans dézoomer pour autant, et c’est tout.

Remarque : sur un smartphone, il est possible de tourner l’écran comme en mode paysage, la hauteur plus grande devenant la largeur. La largueur plus petite devient la hauteur, mais ça peut aller le plus souvent. Mais ajouter l’élément indiqué plus haut, reste nécessaire, à moins l’écran en position mode paysage, ne soit vraiment assez large, genre plus de 800 pixels (donc 800 pixels de hauteur en position téléphone).

Dans le passé, il a existé un équivalent CSS de cet élément HTML. L’équivalent était ainsi :

Code : 

@viewport {
width: device-width;
zoom: 1;
}

Mais ça fait longtemps que ça ne marche plus, cette at‑rule est tombée en désuétude depuis au moins quinze ans, si ce n’est plus.

Si l’ajout de l’élément indiqué ne suffit pas, parce que la page est un peu complexe, alors il faut définir des règles spécifiques pour les petits écran, dans le CSS avec des at‑rules @media. Souvent, il suffit de définir quelques règles (mais qui changent beaucoup de choses) pour les éléments problématiques, pas besoin de re‑écrire tout le fichier CSS pour ça. Ce n’est pas toujours évident, mais moins insurmontable que ça n’en a l’air.

Reste une question en suspend : pourquoi avoir déplacé ça du CSS vers le HTML ? Ça a plus ça place dans les règles de présentations que dans la structure sémantique du document. Le mieux est peut‑être de le placer automatiquement partout (je l’ai sut il y a longtemps, puis l’avait oublié avant de re‑rencontrer ce problème).
Si en utilisant TypeScript pour vérifier le typage, il vous arrive d’obtenir cette erreur : “ error TS2306: File […] is not a module. ”, une possible est solution est d’y ajouter quelque chose comme ça :

Code : 

export const ignored = true; // Dummy statement needed for TypeScript.


Si un fichier JavaScript n’exporte rien, parce qu’il est par exemple utilisé pour définir des types avec la syntax JSDoc, TypeScript se plaint que le fichier n’est pas un module seulement parce qu’il n’exporte rien, d’où l’astuce d’ajouter quelque chose qui ne sert à rien, mais qui est exporté.

Ce comportement changera peut‑être dans l’avenir, mais il existe depuis au moins dix ans ou plus.
L’interface de la classe Set est étrange sur un aspect. Elle présente une propriété entries qui renvoi des tableaux de deux éléments, le premier et le deuxième étant identique. C’est comme si la classe Set était confondue avec la classe Map. Le principe peut se un se comprendre, mais c’est exagéré et si on a besoin de pouvoir confondre les deux, mieux vaut créer une facade, et il faudrait encore justifier pourquoi on voit un ensemble comme un dictionnaire de clés et valeurs ou la valeur et sa propre clé et réciproquement, même si ça peut faire sens pas curiosité ou amusement, mais moins dans des cas concrets (à moins que … et si c’est le cas, il n’est pas interdit d’en témoigner ici).

Pour faire une itération sur un ensemble, mieux vaut utiliser sa propriété values.
Je ne sais pas depuis quelle version, on peut maintenant écrire les nombres en JavaScript (ECMAScript), avec des blancs soulignés comme ça se fait dans d’autres langages tel que Ada ou SML (ou plutôt SuccessorML). Ça rend les grands nombres plus lisibles. Par exemple 1_000_000 au lieu de 1000000. Dans le premier cas, on a moins de mal à compter les chiffres, dans le second cas, on pourrait facilement confondre 100_000 et 1_000_000. Même si votre éditeur ne supporte pas ce format pour sa coloration syntaxique, ça ne signifie pas que l’écriture est incorrecte, comme vous pouvez le vérifier (pour la colorisation et le reste, les éditeurs de texte sont souvent pas à jour pour CSS et JavaScript, même s’ils très bien par ailleurs).
Une autre nouveauté dont la date d’introduction est inconnue, est la possibilité de récupérer le résultat d’une fonction dans plusieurs variables en une seule fois. C’est la décomposition (destructuring) qui existe déjà depuis longtemps en Python et depuis encore bien plus longtemps en SML.

Par exemple :

Code : 

function f () { return {a: 1, b: 2, c: 3}; }
const {a, b, c} = f();

Les constantes a, b et c, valent 1, 2 et 3.

Ça ne fonctionne que si l’objet de destination, dispose au moins de l’ensemble des propriétés des mêmes nom que dans l’objet renvoyé par la fonction :

Code : 

function f () { return {a: 1, b: 2, c: 3}; }
const {a2, b, c} = f();

Dans ce cas, les constantes b et c, valent 2 et 3, mais a2 est indéfinie.

C’est utilisable avec les tableaux aussi :

Code : 

const [x, y] = [1, 2]; // Exemple simple mais sans intérêt.


Avec ces choses, le contrôle du typage avec TypeScript, peut sûrement aider à capter des erreurs.
Un nouveauté récente de JavaScript permettant de créer des Promise en évitant trop d’imbrication. L’interface classique fait que l’objet promise est renvoyé à l’extérieur et qu’une fonction reçoit en arguments à l’intérieur, deux méthodes resolve et reject. Avec la nouveauté introduite, les trois sont renvoyés à l’extérieur comme résultat du constructeur, sous forme d’un objet portant les trois propriétés en question : {promise, resolve, reject}.

Cette nouvelle interface est fournie par Promise.withResolvers() et date de 2024, de l’année dernière. Elle pourrait devenir courante dans un ou deux ans.

Voir : Promise.withResolvers (mozilla.org).