L'API High Resolution Time (ou l'API de temps à haute résolution) est une interface JavaScript qui permet d'accéder à une horloge de haute précision pour mesurer des périodes de temps avec une précision allant jusqu'à la microseconde.
Cela peut être utile pour des applications qui nécessitent une précision temporelle élevée, comme les jeux, les applications de réalité virtuelle ou tout simplement pour mesurer des écarts de performances lorsque les fonctions classiques telles que getTime()
de l'objet Date
ne suffisent plus.
Cette API est bien reconnue depuis 2013 par l'ensemble des moteurs de navigateurs. Ces derniers pourront néanmoins arrondir les valeurs retournées et diminuer la précision pour des raisons de sécurité et de mitigation d'usage détourné.
Pour utiliser l'API High Resolution Time dans votre code JavaScript, vous pouvez commencer par accéder à l'objet performance
et sa méthode now()
.
Voici un exemple simple d'utilisation de l'API High Resolution Time pour mesurer la durée d'une opération dans votre code :
// Récupération de l'heure actuelle en utilisant l'API High Resolution Time
const start = performance.now();
// Effectuer une ou plusieurs opération(s) ici...
// Récupération de l'heure actuelle une nouvelle fois
const end = performance.now();
// Calcul de la durée de l'opération
const duration = end - start;
// Affichage de la durée totale de l'opération (en ms)
console.log(duration);
Vous pouvez également utiliser l'API High Resolution Time pour mesurer des périodes de temps plus longues en utilisant les méthodes setInterval
et setTimeout
, qui vous permettent d'exécuter du code à des intervalles réguliers. Par exemple :
// Définition d'une fonction qui sera appelée toutes les 10 secondes
function logTime() {
// Récupération de l'heure actuelle en utilisant l'API High Resolution Time
var now = performance.now();
// Affichage de l'heure actuelle dans la console
console.log(now);
}
// Exécution de la fonction toutes les 10 secondes
setInterval(logTime, 10 * 1000);
Vous pouvez en savoir plus sur cette API en consultant la spécification W3C High Resolution Time bien entendu.
L'API Fullscreen standard maintenu par le WhatWG vous permet de faire en sorte qu'un élément de votre page web, quel qu'il soit, occupe tout l'espace disponible de l'écran. Cela peut être utile pour les vidéos en plein écran bien entendu mais également les jeux, ou toute autre application qui nécessite un affichage en plein écran sans nécessairement être constituée de vidéo.
Pour utiliser l'API Fullscreen, vous devez d'abord vérifier que le navigateur prend en charge cette fonctionnalité. Vous pouvez le faire en utilisant la propriété fullscreenEnabled
de l'objet document
:
if (document.fullscreenEnabled) {
// Le navigateur prend en charge l'API Fullscreen
} else {
// Le navigateur ne prend pas en charge l'API Fullscreen
}
Si le navigateur prend en charge l'API Fullscreen, vous pouvez ensuite faire en sorte qu'un élément de votre page web passe en mode plein écran en utilisant la méthode requestFullscreen()
:
const element = document.querySelector('#ma-video');
element.requestFullscreen();
Dans cet exemple, nous avons récupéré l'élément en question par un sélecteur et nous avons appelé la méthode requestFullscreen()
.
Il est important de noter que l'internaute doit accepter de passer en mode plein écran avant que cela se produise. Le navigateur affichera une notification demandant si l'internaute souhaite passer en mode plein écran ou non. Si l'internaute refuse, la méthode "requestFullscreen" ne fera rien.
Inversement, il existe une méthode exitFullscreen()
pour quitter le plein écran.
Pour détecter l'acceptation ou le refus de passer en mode plein écran, vous pouvez écouter l'événement fullscreenchange
toujours sur l'objet document
:
document.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) {
// Accord de passer en mode plein écran
} else {
// Refus de passer en mode plein écran
}
});
Dans cet exemple, nous avons ajouté un écouteur d'événement sur l'événement fullscreenchange
. Lorsque l'événement se produit, nous vérifions si l'objet fullscreenElement
de l'objet document
est défini. Si c'est le cas, cela signifie que l'utilisateur a accepté de passer en mode plein écran.
L'API est accompagnée d'un pseudo-élément ::backdrop
qui est rendu immédiatement sous l'élément en plein écran, ce qui permet de créer un arrière-plan sombre, un ombrage ou toute autre fantaisie qui va recouvrir le document d'origine lorsqu'on est en mode plein écran.
Pour utiliser le pseudo-élément ::backdrop, il suffit de l'ajouter à la feuille de styles.
#ma-video:fullscreen::backdrop {
background-color: rgba(0, 0, 0, 0.8);
}
L'API est également complétée par une pseudo-classe :fullscreen
qui va correspondre dynamiquement à tout élément passé en plein écran.
Le format de fichier APNG (Animated Portable Network Graphics) est comme son nom le suggère un format d'image animé reposant sur le format PNG, déjà très bien connu.
APNG a été conçu à partir de 2004 à l'initiative de Mozilla qui héberge la spécification APNG en tant que version améliorée du format PNG et offre des fonctionnalités supplémentaires telles que (évidemment) la possibilité d'animer les images... à l'instar du GIF.
Son support s'est amélioré progressivement, entre 2009 (Firefox) et 2020 (Edge) ce qui représente une longue période de temps, et le voit désormais reconnu par tous les navigateurs. Même les anciens programmes reconnaissant PNG peuvent afficher un fichier APNG car sa première image est au format PNG valide, il y a donc en quelque sorte une rétro-compatibilité qui est relativement assurée par le fait que les données d'animation sont stockées dans un espace supplémentaire déjà prévu par le format PNG initial. Le type MIME correspondant est image/apng
.
Il ne s'agit pas nécessairement du format le plus optimisé pour les "images animées" car il existe bien des concurrents dans ce domaine avec des algorithmes appropriés : on privilégie de nos jours notamment les codecs vidéo (MP4, Webm, AV1) et leurs déclinaisons en formats d'images (WebP animé, AVIF animé : voir des démos). C'est d'ailleurs pour cela que les sites majeurs fournisseurs de GIFs et memes variés (Giphy, Tenor) ne vous servent en réalité, depuis bien longtemps, pas des images GIF mais de véritables et courts fichiers vidéo à leur place.
Le format PNG (Portable Network Graphics) est à la base un format d'image bitmap (ou raster) dans lequel on cherche à mémoriser les données pixel par pixel, avec une qualité supérieure à celle des formats d'image populaires tels que JPEG et GIF notamment parce que la compression ne doit pas affecter la qualité et perdre de l'information. Il utilise donc une compression sans perte pour conserver tous les détails de l'image d'origine, ce qui en fait un meilleur choix pour les images de haute qualité, icônes et schémas.
En termes techniques, la principale différence vis-à-vis de son parent le PNG est l'ajout des capacités d'animation en combinant successivement plusieurs images. Le format APNG est donc un format plus avancé ou plutôt une extension de PNG tel que la spécification PNG du W3C l'indique.
Un autre ensemble de quelques tests consultables en ligne GIF vs APNG vs WebP arrive à la conclusion qu'un fichier APNG est plus léger qu'un WebP animé en mode lossless c'est-à-dire sans perte de qualité.
S'il existe désormais de nombreux convertisseurs en ligne, l'outil le plus basique est APNG Assembler qui en ligne de commande (ou en version graphique sous Windows) va "assembler" de multiples fichiers PNG et appliquer un niveau de compression choisi. En complément gif2apng est un vrai convertisseur d'animations GIF vers APNG, et apng2gif son opposé APNG vers GIF.
Le format APNG est binaire : il ne peut être ouvert par un éditeur de texte comme le SVG, il faudra passer par un éditeur hexadécimal tout comme pour JPEG, WebP, etc. Il stocke les données d'animation en combinant plusieurs images en une seule. Chaque image est stockée dans un chunk (que l'on peut traduire par morceau, tronçon) du fichier APNG, avec des informations sur la durée de l'image et les effets de transition à utiliser pour passer à la suivante. Des chunks supplémentaires stockent des informations sur les contrôles de l'animation, telles que la vitesse et le bouclage.
Les chunks sont alors assemblés dans un conteneur qui débute par une "signature" en début de fichier, signalant qu'il s'agit bien d'une image au format APNG, puis un bloc IHDR
en amont des chunks et un bloc IEND
à la fin.
On obtient donc un assemblage de frames qui étaient à l'origine des fichiers PNG indépendants, dont on a conservé uniquement les données IDAT
, réécrites également en fdAT
(frame data chunk) pour les images qui se suivent avec un numéro de séquence.
Schéma provenant de la page Wikipédia APNG
Vous pouvez utiliser le très intéressant PNG file chunk inspector avec vos fichiers PNG ou des exemples fournis pour analyser leur structure en ligne, dont voici un extrait de capture d'écran :
Un exemple de chunk d'image APNG pourrait être résumé ainsi :
IDAT
[Données de l'image]
acTL (animation control)
[Nombre total d'images dans l'animation]
[Nombre de boucles dans l'animation]
fcTL (frame control)
[Numéro de l'image]
[Largeur de l'image]
[Hauteur de l'image]
[X de l'image]
[Y de l'image]
[Durée de l'image en millisecondes]
[Numéro de l'image suivante]
[Effet de transition à utiliser]
Le format APNG (Animated Portable Network Graphics) et le format GIF (Graphics Interchange Format) ont tous deux la même vocation à créer des animations par une succession d'images bitmap (non vectorielles). Cependant, ils diffèrent sur plusieurs points, notamment car ils ne proviennent pas de la même génération. GIF a été inventé en 1987 !
Vite est un outil front-end JavaScript qui vise à améliorer la rapidité de développement en offrant un serveur de développement rapide et une compilation optimisée pour la production. Il prend la suite d'une grande famille dans laquelle on peut évoquer Grunt, Gulp, et dernièrement Webpack.
Vite utilise la fonctionnalité ES Module Import de JavaScript pour charger les modules de manière asynchrone, ce qui permet une expérience de développement plus rapide et une compilation (étape build) plus petite pour la production. Vite est également conçu pour être facile à utiliser et à configurer, ce qui en fait un choix populaire pour les développeurs de front-end.
Vite a été développé par Evan You, le créateur de Vue.js, dans le but de créer un outil de développement de front-end plus rapide et plus efficace que Webpack, notamment pour les projets basés sur Vue.js. Cette idée initiale a été publiée sur Twitter :
Vite a ainsi été lancé en 2020 et a depuis connu un grand succès, non seulement auprès des projets Vue, mais également auprès d'autres projets de développement front-end. Il a désormais remplacé dans Vue 3 la suite d'outils Vue CLI qui était mise en avant pour Vue 2 et qui comprenait Webpack.
Plusieurs techniques ont été rassemblées :
On peut également comprendre que le processus traditionnel est de produire le bundle à partir de tous les modules compilés pour toutes les routes existantes, ce qui nécessite un important temps de préparation. (Schémas issus de la documentation officielle de Vite)
Avec ESM, le serveur est à l'écoute de toute requête, trouve la route correspondante et importe dynamiquement les modules concernés.
Plusieurs frameworks de développement front-end populaires utilisent Vite comme outil de développement et de build, notamment :
Vite n'est pas lié à un framework spécifique et peut être utilisé avec n'importe quel projet de développement front-end, d'ailleurs moult exemples sont fournis officiellement pour Vue et React à la fois.
Si vous faites déjà du Vue 3, il y a de fortes chances que Vite soit déjà utilisé par défaut (vous pouvez le voir tout simplement en lançant la commande de développement ou de compilation). Vous pouvez aussi utiliser Vite avec un projet de développement front-end (existant ou vide), avec la commande suivante :
npm init vite
Cela posera quelques questions, démarrera le serveur de développement à une URL locale et ouvrira votre application dans un navigateur.
On remarque assez aisément que pour un projet complexe comportant de nombreux composants, la phase de compilation initiale est réduite à quelques secondes alors qu'elle peut prendre jusqu'à plusieurs minutes avec Webpack.
Vous pouvez également noter que dans les outils de développement navigateur, onglet Réseau (Network) on voit passer les requêtes pour les différents modules plutôt que vers un seul import compilé.
Si vous souhaitez utiliser des fonctionnalités avancées de Vite, telles que le HMR, vous devrez peut-être ajouter des lignes de code supplémentaires à votre application pour activer ces fonctionnalités.
Le Hot Module Reloading (HMR) est une fonctionnalité de Vite qui permet de mettre à jour les modules de votre application sans avoir à recharger complètement la page. Cela peut être très utile lorsque vous développez une application et que vous souhaitez voir les changements que vous apportez immédiatement.
Comment le HMR fonctionne dans Vite :
Le HMR est réalisé à l'aide du protocole WebSocket qui maintient une connexion persistante, vous pouvez par ailleurs l'examiner dans l'onglet réseau des outils de développement pour voir les échanges de messages.
Le HMR est une fonctionnalité très pratique qui peut grandement améliorer votre expérience de développement et vous faire gagner du temps.
Plusieurs plugins officiels sont disponibles notamment pour Vue 3 et ses SFC (Single File Components), la syntaxe JSX, React, etc.
Il en existe aussi de nombreux autres mis à disposition par la communauté et que l'on retrouvera dans la liste du repo awesome-vite
Si vous souhaitez en savoir plus, consultez la documentation officielle Vite ou encore le replay de la conférence ViteConf 2022.
C'est parti ?
Les blob URLs sont des URLs (Uniform Resource Locators) générées dynamiquement qui permettent d'accéder aux données binaires stockées dans un objet Blob (Binary Large OBject) en JavaScript.
Un objet Blob peut contenir des données de différents types, comme des images, des fichiers audio ou vidéo, du texte ou tout autre type de données binaires. Cela en fait une technique utile et souple pour manipuler des pseudo-fichiers en mémoire, qui n'existent pas en tant que tels sur un serveur web. D'ailleurs on ne pourra pas générer de Blob côté serveur (back-end), cela n'existe que du côté navigateur (front-end).
🦠 Il ne faut pas le confondre avec Physarum polycephalum qui est une étrange espèce unicellulaire existant depuis des millions d'années, que vous pouvez même adopter, et qui n'est ni un animal, ni un végétal, ni un champignon.
Cet être étrange qui n'a pas de cerveau est capable de communiquer et trouver son chemin dans des labyrinthes, tel que l'ont prouvé des expériences. Il résiste au feu et à l'eau, il a une grande résistance et longévité... et a été emporté dans l'espace.
Toutes ces caractéristiques en ont fait une curiosité idéale inspirant moult livres et films. Dès 1958 on peut retrouver Steven McQueen dans The Blob suivi malheureusement un remake du même titre The Blob qui fut un désastre en 1988.
En revanche, il existe un très bon jeu Tales from Space: Mutant Blobs Attack disponible sur consoles, Windows, Linux, macOS, dans lequel on incarne un blob qui doit grossir de plus en plus pour s'échapper de son laboratoire et par la suite dévorer progressivement le monde des humains.
Arte vous dit tout ce qu'il faut savoir dans la vidéo YouTube Le blob, un génie sans cerveau
On peut générer un Blob à partir d'un fichier grâce à l'API File ou à partir du constructeur Blob()
qui va prendre en paramètre les données brutes (par exemple du texte, du code HTML, de la donnée binaire image, etc.) et d'un type MIME.
Par la suite, les blob URLs peuvent être générées à l'aide de la méthode URL.createObjectURL()
de JavaScript, en passant l'objet Blob
en argument. Par exemple :
// Création d'un objet Blob à partir de données
const myBlob = new Blob(['Hopla!'], {type: 'text/plain'});
// Création d'une URL blob à partir de l'objet Blob
const blobURL = URL.createObjectURL(myBlob);
// Affichage de l'URL blob générée
console.log(blobURL); // "blob:https://example.org/957b4d22-c5b5-4c5f-b5b5-f7f3b3bf2b05"
Voici un autre exemple pour du JSON :
const objet = { type: "fruit", nom: "kiwi" };
const blob = new Blob([JSON.stringify(objet, null, 2)], {type : 'application/json'});
Une fois que vous avez une URL de type blob, vous pouvez l'utiliser comme une URL classique pour accéder aux données contenues dans l'objet Blob. Par exemple, vous pouvez l'utiliser comme source d'une image <img src="blob:https://example.org/957b4d22-c5b5-4c5f-b5b5-f7f3b3bf2b05">
ou comme lien dans un élément a
.
Attention, les blob URLs ne sont valides que pendant la durée de vie de l'objet Blob
auquel elles sont liées, et donc très souvent durant la session de navigation. Le navigateur libère la mémoire lorsqu'on ferme la fenêtre, l'onglet, lorsqu'on recharge, etc. Une fois que le Blob est supprimé ou qu'il n'est plus accessible, la blob URL ne fonctionnera plus et devra être générée à nouveau si nécessaire.
Lire un blob s'effectue avec FileReader
qui est aussi l'API de prédilection pour lire des fichiers File
de manière asynchrone. Par exemple :
let reader = new FileReader();
reader.addEventListener("loadend", function() {
// on lira le contenu de reader.result
});
reader.readAsArrayBuffer(leblob);
L'événement hashchange se déclenche lorsqu'il y a un changement de l'ancre (ou hash) d'une URL, c'est-à-dire la portion après le signe #
. On va le retrouver dans la propriété DOM window.location.hash
.
Le hash est un élément particulier de l'URL : il ne provoque pas de (re)chargement de page lorsqu'il change, il n'est pas divulgué au serveur web dans la requête HTTP.
Dans son usage le plus courant, il est associé à l'accès aux ancres HTML. C'est-à-dire que lorsqu'on clique sur un lien <a href="#recherche">
on va pointer le focus sur l'élément possédant id="ancre"
et faire défiler la page jusqu'à la cible. C'est aussi la technique utilisée par les liens d'évitement pour améliorer l'accessibilité.
Lorsque l'ancre d'une page Web change, on peut également intercepter l'événement hashchange
pour exécuter du code.
// Écouter l'événement
window.addEventListener("hashchange", function(event) {
// Récupérer la nouvelle valeur de l'ancre
const hash = window.location.hash;
// Exécuter du code en fonction de la valeur de hash
// ...
});
Dans cet exemple, nous utilisons la méthode addEventListener()
pour écouter l'événement hashchange
qui se produit sur l'objet window
. En effet, puisqu'il se passe au niveau du navigateur, il n'est pas lié à un élément du DOM en particulier. Lorsque l'événement se déclenche, on récupère la valeur de location.hash
sachant que cet objet location
contient d'autres informations sur l'adresse courante.
Il est aussi possible de récupérer dans l'objet événement event
en premier paramètre de la fonction de callback diverses informations telles que l'ancienne URL oldURL
et la nouvelle newURL
.
Pour utiliser hashchange
afin de charger un nouveau contenu de manière asynchrone en HTTP à l'aide de la fonction fetch()
:
// Écouter l'événement hashchange
window.addEventListener("hashchange", function(event) {
// Récupérer la nouvelle valeur de l'ancre
const hash = window.location.hash;
// Charger le nouveau contenu à l'aide de la fonction fetch
fetch("/api/content/" + hash)
.then(function(response) {
return response.text();
})
.then(function(html) {
// Mettre à jour le contenu de la page
document.querySelector("#content").innerHTML = html;
});
});
Dans ce cas de figure, on récupère toujours à l'aide de hashchange
la nouvelle valeur de l'ancre, puis elle est transmise de manière asynchrone au serveur, ou à une API quelconque de votre choix, pour obtenir le nouveau contenu et l'injecter dans un élement déjà existant de la page grâce à innerHTML
. Il faut donc faire attention à ce qui est renvoyé par le back-end soit une portion de document HTML et non l'intégralité d'une page avec head
, link
, style
, script
et compagnie ce qui ne serait pas conforme et pourrait créer de gros conflits de style et de JavaScript.
Notez que cet exemple utilise then()
pour enchaîner les réponses de la promesse renvoyée par fetch()
. Vous pouvez également utiliser l'opérateur await
pour attendre que la promesse de fetch soit résolue, avant de mettre à jour le contenu de la page, ce qui est un peu plus lisible que de passer par then
.
// Écouter l'événement hashchange
window.addEventListener("hashchange", async function(event) {
// Récupérer la nouvelle valeur de l'ancre
const hash = window.location.hash;
// Charger le nouveau contenu à l'aide de la fonction fetch
const response = await fetch("/api/content/" + hash);
const html = await response.text();
// Mettre à jour le contenu de la page
document.querySelector("#content").innerHTML = html;
});
Tailwind ou pas Tailwind ? Ce framework CSS, fondé sur la méthodologie atomique, divise les rangs des intégratrices et intégrateurs. Chaque camp y trouve des arguments souvent très tranchés : on aime pas du tout ou bien on adore, il reste peu de place pour le "ça dépend".
Au sein de notre agence web Alsacréations, spécialisée dans les domaines front-end et dans l'accessibilité, nous avons expérimenté Tailwind depuis un peu plus de deux années à présent. Cet article détaille les leçons que nous avons tirées de l'usage ce framework.
Quelques précisions concernant notre contexte d'agence : nous ne sommes pas une start-up, nous intégrons des maquettes fournies par le client ou designées en interne et validées par le client. Même s'il nous arrive d'appliquer un framework pour un client (Bootstrap, Tailwind), notre expérience de plusieurs (dizaines d')années en intégration nous interdit le design de nouvelles pages "à l'arrache" comme certains pourraient être tentés de le faire avec ce genre d'outils.
Le mois de juin 2020 marque notre première intégration pour un client réalisée à l'aide de Tailwind CSS, puis une demi-douzaine de projets ont suivi. Sans oublier quelques projets personnels ou secrets.
En novembre 2020 nous publions sur Alsacréations l'article "Tailwind CSS, découverte du framework original et innovant". Quelques mois plus tard paraissent nos Guidelines Tailwind CSS publiques.
Enfin, en mai 2021, nous publions une sorte de synthèse sur la question : "Quels framework et méthodologie CSS choisir ?".
Aucun projet d'intégration ne se déroule à la perfection, surtout dans la durée, et même au sein d'une agence web qui se considère comme compétente en la matière.
Rien que du côté CSS, les écueils que peut traverser un projet sont nombreux :
Notre expérience nous a souvent permis d'atténuer ces problèmes. Et quand je parle d'expérience, j'y inclus les différentes méthodologies que nous avons pu adopter chez Alsacréations : OOCSS dans un premier temps, BEM (un peu) et enfin Tailwind CSS.
Nous avons pu constater au cours de ces deux années d'usage que les promesses faites par Tailwind sont parfaitement tenues.
Les points suivants comptent parmi les plus retenus et cités dans notre agence :
Sans surprise, TailwindCSS n'est pas exempt de désagréments. Voici une petite énumération que nous avons pu étabir en interne :
items-baseline backdrop-invert-0 md:row-start-5 sm:content-around leading-snug dark:tracking-wider
🤷♂️Pour finir sur ce sujet, un témoignage assez évocateur :
Tailwind peut avoir un impact non négligeable sur certaines performances de chargement lorsqu'il y a de nombreuses répétitions de blocs (exemple vécu sur un projet client dont le menu généré pesait 40 Ko à cause des seules classes Tailwind)
Nous ne sommes sans doute pas les seuls dans ce cas, mais au quotidien on adapte Tailwind à notre sauce interne :
Nous sommes parfaitement conscients que nous n'employons pas ce framework "comme il faudrait" mais plutôt d'une façon hybride, cumulée avec d'autres méthodologies dans lesquelles nous piochons des idées à appliquer.
Nous nous trouvons actuellement dans une phase de transition où l'on s'est remis à tester de nouvelles méthodologies et approches.
En toute transparence, Tailwind CSS ne nous convient pas à 100%. Ou plutôt, cela dépend des types de projets : sur certains projets, il nous paraît naturel de l'employer tandis que sur d'autres typologies il devient contre-productif.
Il est tout à fait probable qu'aucune méthodologie ne nous convienne parfaitement pour l'ensemble de nos prestations d'intégration et seul l'avenir nous le dira.
En attendant, un nouveau challenger est entré en jeu et nous commençons à le tester en production : Cube CSS.
CubeCSS n'est pas un framework mais une méthodologie plus générale. Pour le moment, nous la trouvons très prometteuse, mais laissez-nous le temps de l'expérimenter et… de vous partager nos conclusions dans un prochain article !
L'attribut sandbox
de l'élément iframe
en HTML est utilisé pour restreindre les actions qui peuvent être effectuées dans l'iframe. Son rôle est d'améliorer la sécurité et la stabilité de la page web, surtout lorsque l'iframe est fournie par un service tiers que l'on ne contrôle pas, et qui pourrait être abandonné, détourné, voire hacké à l'avenir. Limiter les fonctionnalités à risque d'un iframe provenant d'un domaine tiers aide à prévenir les attaques de type clickjacking ou cross-site scripting (XSS).
Dans le document ou l'application web, on ajoute une balise <iframe>
à l'endroit où l'on souhaite inclure le contenu d'un autre site. Par exemple :
<iframe src="https://www.example.org/contenu.html"></iframe>
sandbox
, signifie par défaut que tout est permis : attention dino danger.sandbox
ou sandbox=""
(sans valeur) signifie que toutes les restrictions sont appliquées par défaut : sécurité maximum.allow
va lever les restrictions une par une.Par exemple, pour autoriser l'exécution de JavaScript, l'ouverture de pop-up et l'accès aux éléments de la page parente, on va utiliser la syntaxe suivante :
<iframe src="https://www.example.org/contenu.html" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe>
Voici d'autres autorisations possibles :
allow-same-origin
: permet au contenu de l'iframe d'être traité comme provenant de la même origine.allow-downloads
: autorise les téléchargements.allow-scripts
: autorise l'exécution de JavaScript dans l'iframe.allow-popups
: autorise l'ouverture de pop-up à partir de l'iframe.allow-forms
: autorise l'utilisation de formulaires dans l'iframe.allow-top-navigation
: autorise le contenu de l'iframe à charger une nouvelle page dans la fenêtre du navigateur.allow-pointer-lock
: autorise l'utilisation de la fonctionnalité de verrouillage du curseur/pointeur.allow-orientation-lock
: autorise le blocage de l'orientation de l'écran (sur smartphone/tablette notamment).allow-presentation
: autorise le lancement d'une session de présentation.Cet attribut est épaulé par une directive CSP (Content-Security-Policy) qui définit ces valeurs au niveau des en-têtes HTTP renvoyées par le serveur, par exemple Content-Security-Policy: sandbox;
ou Content-Security-Policy: sandbox allow-downloads;
Le WebVTT (Web Video Text Tracks) est un format de sous-titrage pour les vidéos en ligne. Il permet aux internautes de lire des sous-titres sur des vidéos diffusées sur le web, que ce soit sur un ordinateur, une tablette ou un smartphone. Cette spécification W3C a atteint le statut de recommandation en 2019 mais la fonctionnalité a commencé à être implémentée à partir de 2012-2014 dans tous les navigateurs, il n'y a donc plus aucune raison de ne pas s'en servir.
Le fonctionnement de WebVTT est assez simple : les sous-titres sont enregistrés dans un fichier texte qui est associé à la vidéo. Ce fichier contient les lignes de dialogue ou d'audio-description, ainsi que des informations sur le moment où elles doivent être affichées à l'écran. Lorsque la vidéo est lue, les sous-titres sont affichés en temps réel en fonction des informations contenues dans le fichier WebVTT.
Les lecteurs intégrés aux plateformes tout-en-un telles que YouTube embarquent leurs propres solutions puisqu'ils ne reposent pas sur une unique balise <video>
mais un composant associant barre de contrôles sur-mesure, lecteur avec streaming, transmission de statistiques, etc.
Il existe plusieurs programmes et applications web qui permettent de générer le format WebVTT, ou de l'exporter à partir d'autres assez similaires dont il s'inspire comme le SRT. Cependant contrairement au SRT, le format WebVTT utilise un point à la place de la virgule pour séparer les secondes et les millisecondes et dispose de bien plus de capacités pour la mise en forme.
Exemple de source WebVTT :
WEBVTT
00:00:00.000 --> 00:00:05.000
Alors vous deux, vous déterrez les dinosaures ?
00:00:05.000 --> 00:00:10.000
On essaie.
00:00:10.000 --> 00:00:15.000
Le kiwi rencontre un écureuil qui lui donne des noix.
00:00:15.000 --> 00:00:20.000
Il faudra vous habituer au professeur Malcolm. Il souffre d'un déplorable excès de personnalité. Surtout pour un mathématicien.
Dans cet exemple, chaque ligne commence par un intervalle de temps au format "HH:MM:SS.mmm" (heures, minutes, secondes et millisecondes) indiquant quand le texte associé doit commencer à être affiché et lorsqu'il doit disparaître.
Il est important de noter que WebVTT est uniquement un format de texte. Cela signifie qu'il est juste prévu pour afficher des sous-titres en surimpression sur une vidéo existante.
Pour utiliser WebVTT, vous devez d'abord enregistrer les sous-titres dans un fichier texte, encodé en UTF-8, avec l'extension .vtt
. Vous pouvez créer ce fichier vous-même en utilisant un éditeur de texte.
Une fois le fichier WebVTT prêt, vous devez l'associer à la vidéo à l'aide de la balise HTML track
, enfant direct de <video>
, aux côtés d'une ou plusieurs sources selon les formats utilisés.
<video>
<source src="video.mp4" type="video/mp4">
<track src="sous-titres.vtt" kind="subtitles" srclang="fr" label="Français">
</video>
Dans l'exemple précédent, la balise source
indique l'url vers le fichier vidéo, et la balise track
indique l'url vers le fichier WebVTT contenant les sous-titres. Le navigateur s'occupe du reste.
☝ Les visuels suivants sont des captures d'écran et non des lecteurs vidéo, ne tentez pas de lancer la lecture en cliquant dessus ;)
L'ajout d'une balise track
valide fait apparaître l'option de sous-titrage dans le menu contextuel.
La balise "track" indique au navigateur web que les sous-titres doivent être affichés pendant la lecture de la vidéo et précise par des attributs sa nature : kind
doit comporter la valeur subtitles
, tandis que srclang
contiendra le code langue et label
la langue exprimée de façon lisible pour les humains. Vous pouvez ainsi utiliser l'élément track
pour ajouter des sous-titres dans d'autres langues.
Sous Chromium :
Sous Firefox :
On peut aussi styler les sous-titres grâce à CSS, soit dans le document parent avec le sélecteur video::cue
...
<style>
video::cue {
background: black;
color: white;
}
video::cue(b) {
color: tomato;
}
</style>
...soit en embarquant des déclarations de styles dans le fichier WebVTT lui même (ils devront figurer en amont du texte).
WEBVTT
STYLE
::cue {
background: black;
color: white;
}
00:31:30.000 --> 00:31:35.000
Je veux me libérer !
On peut également ajouter des notions de chapitrage, des métadonnées, des commentaires avec NOTE
, fournir des indications de positionnement avec region
puis d'alignement avec align
notamment lorsqu'il s'agit de dialogues où il faut identifier la personne qui parle.
WebVTT est donc une solution simple et très personnalisable pour intégrer des sous-titres et améliorer l'accessibilité de vos vidéos.
Chaque navigateur web dispose d'un "parseur HTML" (un interpréteur) qui parcourt le code source et le transforme en modèle d'objets.
Ce parseur HTML du navigateur est bloqué par deux types de ressources :
<link>
<script>
qui ne disposent pas d'attributs async
ni defer
Depuis 2008, un second mécanisme parallèle entre en jeu sur l'ensemble des navigateurs : celui du "Preload Scanner". Ce second parseur agit lorsque le parseur HTML est bloqué sur une ressource et pré-charge les ressources suivantes indiquées dans le markup HTML.
Cela signifie que certains éléments placés hors HTML ne participeront pas à cette recherche anticipée : c'est le cas des polices et des images de fond appelées dans les fichiers CSS, mais également des liens ou ressources situées dans des scripts.
Ce mécanisme est automatique, mais il est possible de l'influencer en proposant le pré-chargement de certaines ressources en priorité en agissant sur les "Priority Hints" tels que async
, defer
, rel=preload
ou fetchpriority
async
et defer
Ces attributs sont liés au chargement des scripts. Dans les deux cas, ces attributs rendent le chargement asynchrone et ne bloquent pas le parseur HTML :
async
est exécuté dès que le navigateur en a la possibilité. Les ressources sont potentiellement chargées dans n'importe quel ordre.defer
est exécuté lorsque tout le DOM est parsé. Les ressources sont chargées dans l'ordre dans lequel elles sont placées dans le DOM.<script module>
(EcmaScript Modules) sont en defer
par défaut.Détail important : async
est prioritaire sur defer
.
<script async src="script.js">
<script defer src="script.js">
Pour en savoir plus à ce sujet, n'hésitez pas à consulter l'article détaillé "Les attributs async et defer pour <script>".
rel=preload
Cette déclaration demande au navigateur de découvrir et charger une ressource en priorité avant même que le parseur ne l'atteigne. Elle est également particulièrement utile pour tous les assets non indiqués dans le markup HTML.
Exemple de pré-chargement de police :
<!-- Dans le <head> après
la feuille de styles pour ne pas la bloquer -->
<link rel="preload" as="font" href="kiwi.woff2"
type="font/woff2" crossorigin="anonymous">
(Note : ici crossorigin="anonymous"
n'est utile que si la police n'est pas auto-hébergée)
Exemple de pré-chargement d'image :
<link rel="preload" as="image" href="hero.webp">
Priorité : le niveau de priorité de rel=preload
est "Mandatory" (obligatoire), c'est à dire que cette fonctionnalité doit absolument être traitée en premier lieu par le navigateur. Cette action est susceptible de bloquer le chargement des feuilles de styles.
Pour mieux comprendre cette propriété dans un cas concret (polices de caractères), n'hésitez pas à consulter l'article détaillé "Optimisez vos polices web".
fetchpriority
L'attribut fetchpriority
informe le navigateur du degré de priorité du pré-chargement d'une ressource. Il est possible de l'appliquer sur l'élément <link>
mais aussi directement sur <img>
, <script>
et <iframe>
.
Les valeurs possibles sont "high" (haute priorité), "low" (basse priorité) et "auto" (valeur par défaut).
Quelques exemples :
<!-- Ce script doit être pré-chargé
mais d'autres ressources sont prioritaires -->
<link rel="preload" href="script.js" as="script" fetchpriority="low">
<!-- Cette image de fond critique
est hautement prioritaire -->
<link rel="preload" as="image" href="hero.webp" fetchpriority="high">
<!-- Cette image doit être pré-chargée
mais n'est pas vraiment critique -->
<img src="sausage.svg" alt="je ne suis pas importante" fetchpriority="low">
Avec l'exemple d'images d'un carousel :
<ul class="carousel">
<img src="img/carousel-1.jpg" fetchpriority="high">
<img src="img/carousel-2.jpg" fetchpriority="low">
<img src="img/carousel-3.jpg" fetchpriority="low">
<img src="img/carousel-4.jpg" fetchpriority="low">
</ul>
(source de l'exemple : https://web.dev/priority-hints/)
Priorité : le niveau de priorité de fetchpriority
est "Hint" (indice), c'est à dire que cette fonctionnalité est une simple indication pour le navigateur.
preconnect
et dns-prefetch
Ces attributs sont liés au chargement des ressources externes (non hébergées localement).
rel="preconnect"
informe le navigateur que l'on souhaite établir une connexion le plus rapidement possible à une autre plateforme.rel="dns-prefetch"
ne fait que résoudre le nom de domaine sans toutefois atteindre la ressource indiquée.<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
<link rel="dns-prefetch" href="https://example.com">
Priorité : le niveau de priorité de preconnect
et dns-prefetch
est "Hint" (indice), c'est à dire que ces fonctionnalités sont de simples indications pour le navigateur.
Pour en savoir plus sur l'usage de ces attributs, n'hésitez pas à consulter l'article détaillé "Optimisation des pré-chargements avec prefetch, dns-prefetch et prerender".
L'attribut loading
permet de ne charger que les images situées au dessus de la ligne de flottaison. Les autres images ne sont alors chargées que lorsque cela devient nécessaire, au fur et à mesure que l'utilisateur scrolle (défile). On améliore ainsi le temps de chargement initial de la page.
Les valeurs de loading
sont les suivantes :
eager
: l'image est chargée immédiatement, qu'elle soit située dans ou hors de la fenêtre visible (valeur par défaut),lazy
: le chargement est retardé jusqu'à ce que l'usager scrolle et s'approche du bas de la fenêtre du navigateur.<img src="image.webp" loading="lazy" width="" height="" alt="descriptif de l'image">
<iframe src="video-player.html" loading="lazy" title=""></iframe>
Pour en savoir plus sur cet attribut appliqué aux images, n'hésitez pas à consulter l'article détaillé "Attribut loading=lazy pour les images".
Dans cette première partie nous allons voir comment mettre en place un menu accessible simple, sans sous-menu : récapitulatif des bases, explications des notions importantes et des moyens pour rendre notre menu à un niveau accessible. C’est parti !
Tout d’abord voyons en quelques étapes comment mettre en place un menu sur WordPress.
Dans le fichier de configuration du thème (souvent functions.php
ou un sous-fichier si vous prônez l’organisation comme chez Alsacréations) enregistrons un menu "Menu principal" avec la fonction register_nav_menu()
. Il a pour slug main-menu
qui nous sera utile pour afficher le menu :
function register_my_menu() {
register_nav_menu( 'main-menu' => __( 'Menu principal', 'text-domain' ) );
}
add_action( 'after_setup_theme', 'register_my_menu' );
Dans l’administration WordPress, l’emplacement apparait dans les réglages du menu :
Il n’y a plus qu’à appeler le menu là où nous souhaitons l’afficher, dans l’en-tête header.php
par exemple.
Pour cela il faut utiliser la fonction wp_nav_menu()
et préciser le slug précédemment enregistré :
/**
* Affiche le menu "Menu principal" enregistré au préalable.
*/
wp_nav_menu([
'theme_location' => 'main-menu',
]);
Cette fonction propose des options de configuration très avancées. Vous retrouverez la liste des paramètres disponibles sur la documentation officielle de wp_nav_menu
.
Pour le moment nous souhaitons créer un menu simple. Pour cela créer un nouveau menu dans l'interface d'administration de WordPress, le nommer, et cocher l’emplacement enregistré à l’étape 1.
Si vous avez bien suivi les étapes, vous devriez avoir un menu qui fonctionne à minima. Et voici le code généré par WordPress :
<div class="menu-menu-principal-container">
<ul id="menu-menu-principal" class="menu">
<li id="menu-item-256" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-home current-menu-item page_item page-item-251 current_page_item menu-item-256">
<a href="">Accueil</a>
</li>
<li id="menu-item-290" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-290">
<a href="">Actualités</a>
</li>
<li id="menu-item-253" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-253">
<a href="">Contact</a>
</li>
</ul>
</div>
Côté accessibilité, le code généré possède quelques points à améliorer :
<div>
.Mais comment corriger cela ? La fonction wp_nav_menu()
possède des paramètres qui pourront nous aider, mais cela ne nous permettra pas de tout corriger. Dans ce cas, il faudra modifier le code généré par WordPress.
Comment WordPress gère ce code ? Comment modifier ce code ?
Nous allons utiliser le walker Walker_Nav_Menu
qui nous sera utile pour personnaliser le code généré. Mais avant, explications !
Un walker est une classe abstraite PHP qui permet de parcourir une arborescence de données (objets ou tableaux) afin de générer du code HTML.
Par rapport à notre menu ➡️ le walker va parcourir le menu, et exécuter à chaque noeud une fonction.
Quand nous observons la classe dans le codex, le walker possède 4 fonctions :
start_lvl
: correspond au début d’un sous-menu <ul>
et son contenu.end_lvl
: Fin d’un sous-menu </ul>
.start_el
: Début d’un élément du menu. Équivalent du <li>
et son contenu.end_el
: Fin d’un élément </li>
.Walker_Nav_Menu
à travers un menu à deux niveaux.Et l’avantage d’un walker, c’est qu’il est possible de l’étendre afin de customiser ces fonctions. Spoiler : c’est ce que nous allons faire 🤠.
Actuellement, notre menu est perçu simplement comme une liste, avec un conteneur ne donnant aucun contexte.
Nous allons donc ajouter un peu de sémantique pour restituer, à l’utilisateur et aux lecteurs d’écran, que c’est un menu de navigation. Pour cela, faisons appel aux landmarks ARIA.
Rappel : les landmarks permettent de déclarer des régions (header
, main
, footer
, nav
, aside
, etc.) afin de structurer une page et de restituer le contexte aux lecteurs d’écran.
Ici, nous allons englober notre menu d’une balise <nav>
avec un attribut ARIA role="navigation"
. Si plusieurs menus de navigation existent dans le site, une bonne pratique est de l’identifier via un attribut aria-label
ou la combinaison aria-labelledby/id
.
Si vous avez regardé en détail le codex de wp_nav_menu()
, vous vous demanderez sûrement pourquoi ne pas utiliser les paramètres container
et container_aria_label
. Comme ils sont de type string
, il n’aurait donc pas été possible de passer l’attribut role="navigation"
.
Nous privilégions donc de ne pas utiliser le conteneur généré par WP :
<nav role="navigation" aria-label="<?php _e('Menu principal', 'text-domain'); ?>">
<?php
wp_nav_menu([
'theme_location' => 'main-menu',
'container' => false // On retire le conteneur généré par WP
]);
?>
</nav>
Visuellement la page courante d’un menu peut-être indiquée par la couleur, la forme (bordure basse), une icône, etc. :
Mais qu’en-est-il des personnes malvoyantes utilisant un lecteur d’écran ? Il faut une alternative HTML en plus de l’indication visuelle.
L’attribut aria-current
rentre en piste ! Il permet d’indiquer aux lecteurs d’écran l’élément actif parmi un ensemble. Dans notre cas, il aura pour valeur :
page
sur la page courante;true
sur la page parente, si existante.Exemples :
<a href="#" aria-current="page">Accueil</a>
<a href="#" aria-current="true">Actualités</a>
<ul class="sub-menu">
<li>
<a href="#" aria-current="page">Sous-actualité</a>
</li>
<li>
<a href="#">Sous-actualité</a>
</li>
</ul>
Mais.. comment faire sur WordPress ? Si vous avez bien suivi c’est le moment d’étendre le walker 🚀
Pour cela, nous allons dans un premier temps créer un nouveau fichier menus.php
et l’appeler dans le fichier functions.php
.
Dans ce fichier nous allons simplement copier/coller le walker existant de WordPress Walker_Nav_Menu.
Quel est l’intérêt me direz-vous ? Simplement pour éviter de faire des erreurs. Nous aurons juste à customiser la/les fonction(s) souhaitée(s).
La seconde étape est de renommer le walker pour le distinguer d’autres potentiels walkers :
class A11y_Walker_Nav_Menu extends Walker {
Ensuite, nous allons appeler ce walker dans la fonction wp_nav_menu()
en ajoutant un paramètre walker
pour créer une instance de notre classe A11y_Walker_Nav_Menu
:
<nav role="navigation" aria-label="<?php _e('Menu principal', 'text-domain'); ?>">
<?php
wp_nav_menu([
'theme_location' => 'main-menu',
'container' => false,
'walker' => new A11y_Walker_Nav_Menu()
]);
?>
</nav>
Notre objectif est d’ajouter un attribut aria-current
sur un élément du menu lorsqu’il correspond à la page courante.
Comme précisé plus haut, c’est la fonction start_el()
qui va permettre de modifier le contenu d’un élément du menu.
Avant tout, regardons plus en détail cette fonction :
/**
* Starts the element output.
*
* @since 3.0.0
* @since 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
* @since 5.9.0 Renamed `$item` to `$data_object` and `$id` to `$current_object_id`
* to match parent class for PHP 8 named parameter support.
*
* @see Walker::start_el()
*
* @param string $output Used to append additional content (passed by reference).
* @param WP_Post $data_object Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $current_object_id Optional. ID of the current menu item. Default 0.
*/
public function start_el( &$output, $data_object, $depth = 0, $args = null, $current_object_id = 0 ) {
// Restores the more descriptive, specific name for use within this method.
$menu_item = $data_object;
// Suite de la fonction...
Les commentaires nous précisent que :
$output
est le résultat de la fonction sous forme de contenu HTML.$data_object
est un objet qui nous permet de récupérer toutes les informations de l’élément. À noter que la première ligne de la fonction nous précise d’utiliser $menu_item
. Cette variable regroupe plusieurs clés (n’hésitez pas à explorer son contenu en utilisant var_dump($menu_item)
).Arrêtons-nous sur la clé current
. C’est un booléen qui a pour valeur true
lorsque l’élément correspond à la page courante. Parfait, c’est ce que nous voulons !
Mais comment ajouter un attribut à l’élément ? Et bien WordPress ne fait pas les choses à moitié : si vous regardez bien la fonction, un paramètre $atts
est mis à disposition. Il utilise le filtre nav_menu_link_attributes
:
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $menu_item, $args, $depth );
Ce filtre permet d’ajouter/modifier des attributs à l’élément actuel. Il faut donc ajouter, avant la ligne ci-dessus, le code suivant :
$atts['aria-current'] = $menu_item->current ? 'page' : '';
Et voilà, l’attribut est ajouté dans le code lorsque $menu_item
possède la clé current
. Plutôt simple, hein ?
title
si le lien s’ouvre dans un nouvel ongletL’utilisation d’un attribut title
permet d’ajouter un complément d’information. Attention à l’utiliser correctement : il faut reprendre l’intitulé du lien, puis ajouter le texte complémentaire (notes du glossaire RGAA).
Dans notre cas nous allons préciser qu’un lien s’ouvre dans un nouvel onglet lorsque son target
vaut _blank
:
Exemple :
<a href="#" target="_blank" title="Intitulé du lien (s'ouvre dans un nouvel onglet)">Intitulé du lien</a>
Préciser cette action permet de prévenir les utilisateurs d’un changement brusque et éviter de perturber leur navigation.
Nous allons donc modifier la fonction start_el()
pour ajouter un title
lorsque le target
est _blank
. Si vous regardez bien, cette condition existe déjà :
if ( '_blank' === $menu_item->target && empty( $menu_item->xfn ) )
Il n’y a plus qu’à ajouter l’attribut title
et lui donner pour valeur le titre de l’élément title
suivi de notre texte :
if ( '_blank' === $menu_item->target && empty( $menu_item->xfn ) ) {
// Attribut title : on précise que le lien s'ouvre dans un nouvel onglet
$atts['title'] = $menu_item->title . __( ' (s\'ouvre dans un nouvel onglet)', 'text-domain' );
}
Voilà ce qu’il en est pour la mise en place d’un menu accessible simple. Cette première partie était assez longue car nous trouvions important de revenir sur les bases de la création d’un menu WordPress. La prochaine entrera directement dans le vif du sujet et concernera la gestion des sous-éléments d’un menu.
Retrouvez l'intégralité de ce tutoriel en ligne sur Alsacreations.com
Sans avoir à télécharger ou à installer quoi que ce soit, vous allez apprendre tout ce que vous devez savoir sur Svelte.
Svelte est un framework orienté composants et réactivité (comme React ou Vue), mais avec des différences majeures dans son architecture : pas de Virtual DOM, une transformation des instructions dès la compilation en JavaScript qui va directement modifier le DOM. Ainsi il n'y a pas d'injection du framework lui-même dans le code produit, l'idée est de favoriser les performances au maximum.
Vous allez utiliser l'application REPL sur le web.
Ce cours explique comment télécharger des applications REPL pour un petit développement. En le suivant jusqu'à la fin, vous serez prêt à commencer à développer vos propres applications Svelte.
Svelte.dev fournit un REPL permettant de définir des composants Svelte et de voir leur rendu. L'utilisation de ce REPL est le moyen le plus facile d'expérimenter Svelte.
Pour commencer, allez sur le site Web principal de Svelte.dev, puis cliquez dans le navigation sur le lien REPL. Vous devriez constater que l'on vous présente une petite application qui se nomme "Hello World".
Le seul fichier fourni dans le REPL est App.svelte
. Ce fichier peut en importer d'autres. Ils sont définis dans des onglets supplémentaires au sein du REPL.
Pour ajouter d'autres fichiers .svelte
ou .js
, cliquez sur le bouton plus (+) à droite des onglets, puis donnez un nom à votre nouveau fichier. Par défaut, les fichiers nouvellement créés ont une extension *.svelte
. Pour signifier que vous utiliserez un fichier *.js
, renommez complètement l'onglet.
Pour supprimer un fichier, cliquez d'abord sur l'onglet correspondant si ce n'est pas le fichier actif, puis cliquez sur la croix "X" qui apparaît à droite de son nom.
Le REPL contient trois onglets à droite :
La barre supérieure du REPL contient des liens vers de nombreuses ressources Svelte (des tutoriels, la documentation de l'API, des exemples, le blog Svelte, la FAQ Svelte, la page d'accueil de Sapper, etc.).
Vous pouvez afficher ou masquer la navigation en cliquant sur le bouton "Plein écran" (qui devient une croix si vous êtes déjà en plein écran).
Créons une application simple pour découvrir certaines fonctionnalités de Svelte.
Ajoutez le code HTML avant l'élément h1 :
<label for="name">Name</label>
<input id="name" value={name}>
Maintenant nous devons saisir un nom, mais cela ne change pas le message d'accueil. Lorsque l'utilisateur saisit le nom, nous devons ajouter un événement afin que la valeur de la variable name
soit mise à jour.
Pour ce faire, modifiez l'élément input
comme ceci :
<input id="name" on:input={event => name = event.target.value} value={name} />
Modifiez le texte dans le champ : cela fonctionne ! Mais ce n'est pas toujours la bonne solution... Nous pouvons faire mieux en utilisant la directive Svelte bind
. Plus tard, nous parlerons des nombreuses utilisations de la directive bind
. L'une d'entre elles consiste à lier la valeur d'un élément (input) d'un formulaire à une variable.
Voilà ce que ça donne :
<input id="name" bind:value={name} />
Nous pouvons même raccourcir le code si le nom de l'attribut est le même nom que la variable à écouter :
<input id="name" bind:value />
C'est bien beau tout ça, mais ça manque de fantaisie, n'est ce pas ? Après le code HTML, ajoutez ceci :
<style>
h1 {
color: #FF5349;
}
</style>
Désormais, le titre doit être en rouge-orangé. Il serait intéressant que l'utilisateur puisse changer la couleur de ce titre. Nous allons ajouter un champ input de type color
. L'avantage de cet élément html, c'est qu'il permet de sélectionner une couleur facilement avec un sélecteur de couleur (color picker, pour les amateurs de la langue de Shakespeare).
Voici le code complet qui permet de changer la couleur du titre et d'afficher un rappel de la couleur choisie dans un petit carré à côté du sélecteur.
<script>
let color = '#FF5349';
let name = 'tout le monde';
</script>
<label for="name">Votre nom</label>
<input id="name" bind:value={name}>
<label for="color">Couleur </label>
<input id="color" type="color" bind:value={color}>
<div style="background-color: {color}" class="swatch" />
<h1 style="color: {color}">Salut {name}!</h1>
<style>
.swatch {
display: inline-block;
height: 20px;
width: 20px;
}
</style>
Ajoutons une fonctionnalité dans la partie <script>
afin de permettre à l'utilisateur de mettre le texte en majuscules :
let upper = false;
$: greeting = `Salut ${name}!`;
$: casedGreeting = upper ? greeting.toUpperCase() : greeting;
(Cela ne suffit pas, attendez la suite...) Mais... Que signifie $:
??
Il s'agit d'une instruction réactive. Les instructions réactives sont ré-exécutées chaque fois que la valeur d'une variable à laquelle elles font référence change. Les déclarations réactives qui attribuent une valeur à une variable sont également appelées déclarations réactives.
Dans le code précédent, nous calculons une nouvelle valeur pour greeting
chaque fois que la valeur de name
change. Puis nous calculons une nouvelle valeur pour casedGreeting
à chaque fois que la valeur de upper
ou greeting
change.
Pratique n'est ce pas ? ajoutons un champ de type checkbox (case à cocher) afin de modifier facilement la valeur de upper
:
<label><input type="checkbox" bind:checked={upper}> Uppercase </label>
N'oublions pas de changer le titre pour utiliser la bonne variable réactive casedGreeting
:
<h1 style="color: {color}">{casedGreeting}</h1>
Nous avons fini avec cette mini-application. Vous pouvez la télécharger en cliquant sur le bouton download" !
Lorsque vous cliquez sur celui-ci, vous devriez récupérer une archive zip. Si vous décompressez dans un dossier et que vous voulez tester "en local", vous devez avoir installé NodeJS et npm sur votre ordinateur.
De la même façon que pour certains autres frameworks tels que Vue, Nuxt ou React :
cd
).npm install
pour installer les dépendances.npm run dev
pour lancer le serveur de développement.Vous pourrez alors modifier les fichiers locaux dans le dossier src/
notamment App.svelte
et constater immédiatement les changements dans le navigateur.
main.js
est le point d'entrée de notre application.public/
contient les fichiers statiques disponibles depuis la racine HTTP, par exemple favicon, images, css.💡 Petite astuce : Lorsque vous ne savez pas quelle commande utiliser pour lancer une application NodeJS, ayez le réflexe de jeter un oeil au fichier package.json
. S'il est correctement rédigé, il devrait contenir une partie script
, qui est une liste des commandes disponibles. Vous devrez préfixer votre commande par npm run
, par exemple npm run build
, puis npm run start
pour lancer la compilation et l'exécution.
Nous venons de faire un bref tour d'horizon de Svelte, mais il y a encore tellement de choses à voir ! A bientôt pour un nouveau chapitre consacré à Svelte !
Retrouvez l'intégralité de ce tutoriel en ligne sur Alsacreations.com
Si vous êtes développeur front-end, vous avez sûrement déjà rencontré le besoin de récupérer des informations depuis le navigateur.
Aujourd'hui, nous allons voir comment utiliser l'API fetch
disponible dans la quasi totalité des navigateurs.
Dans certaines situations, il peut être avantageux de récupérer des informations directement depuis le navigateur plutôt qu'avant le rendu de la page sur le serveur (par exemple avec PHP).
Dans le cas d'un formulaire de recherche, il n'est pas forcément nécessaire de recharger complètement la page pour la régénérer sur le serveur, nous pourrions ici faire une requête Ajax en arrière plan qui serait plus rapide.
Pour ce faire, nous aurons besoin d'une API REST. Pour faire simple, une API REST est une collection d'URI sur lesquelles nous pouvons faire des requêtes HTTP (GET, POST, PATCH,...) et récupérer des informations, préférablement en JSON.
EX: GET "https://jsonplaceholder.typicode.com/todos/1"
Résultat:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Pour répondre à notre besoin, la méthode la plus simple (et la plus moderne) est d'utiliser fetch
.
Sans plus attendre, passons au code. ▶️
Pour lancer une requête, c'est aussi simple que d'appeler la fonction avec notre URI, ainsi que la méthode HTTP correspondante.
fetch('https://jsonplaceholder.typicode.com/todos', { method: 'GET' })
Cependant ceci ne suffit pas pour récupérer le résultat de la requête, puisque fetch
retourne une promesse.
Nous utilisons ici .then()
pour récupérer le résultat de cette promesse.
fetch('https://jsonplaceholder.typicode.com/todos', { method: 'GET' })
.then(function (response) {
// `response` contiendra ces propriétés (en partie)
/*
readonly headers: Headers; -> https://developer.mozilla.org/fr/docs/Web/HTTP/Headers
readonly ok: boolean; -> `true` si la requête s'est bien déroulée, `false` sinon
readonly status: number; -> https://developer.mozilla.org/fr/docs/Web/HTTP/Status
blob(): Promise<Blob>; -> Conversion du résultat en Blob
formData(): Promise<FormData>; -> Conversion du résultat en formData
json(): Promise<any>; -> Conversion du résultat en JSON
text(): Promise<string>; -> Conversion du résultat en texte
*/
})
Dans le cadre de notre besoin, ce qui nous intéresse, c'est de recevoir le résultat en JSON, nous utiliserons donc response.json()
qui retourne également une promesse.
fetch('https://jsonplaceholder.typicode.com/todos', { method: 'GET' })
.then(function (response) {
return response.json()
})
.then(function (json) {
// `json` est le vrai résultat de notre requête !
})
Dans la plupart des cas (et selon vos conventions de code), les requêtes fetch
s'écrivent:
fetch('https://jsonplaceholder.typicode.com/todos', { method: 'GET' })
.then((response) => response.json())
.then((json) => { /* ... */ })
fetch
permet également de passer plusieurs options à notre requête HTTP, par exemple des headers.
Certaines requêtes comme les POST ou PUT peuvent récupérer un body, également pris en charge par fetch
!
const options = {
method: 'POST',
headers: {
// Nous n'accepterons que le JSON en résultat.
'Accept': 'application/json',
// Dans le cas d'une requête contenant un body,
// par exemple une POST ou PUT, on définit le format du body.
'Content-Type': 'application/json',
// Cas d'usage courant pour gérer l'authentification avec une API REST.
'Authorization': 'Bearer ${token}'
},
body: JSON.stringify({
title: 'Un post',
content: 'Contenu de mon post'
})
}
fetch('https://example.com/posts', options)
.then((response) => response.json())
.then((createdPost) => { /* ... */ })
fetch
est une façon très simple (beaucoup moins verbeuse que XMLHttpRequest) de faire des requêtes HTTP et permet d'améliorer la performance d'un site dans certains cas.
Nous sommes toujours curieux chez Alsacréations, n'hésitez pas à nous faire des retours d'expérience en relation avec fetch
😉.
Retrouvez l'intégralité de ce tutoriel en ligne sur Alsacreations.com
Les styles CSS doivent leur nom à la Cascade, qui est un bien joli et complexe algorithme tenant compte de nombreux paramètres tels que l'origine des styles, la spécificité des sélecteurs ainsi que leur ordre d'apparence.
La Cascade, c'est l'essence même de CSS. C'est ce qui fait son utilité, sa beauté… et c'est aussi le pire cauchemar des intégratrices et intégrateurs.
La Cascade, c'est ce qui fait que nos paragraphes arboreront une chatoyante couleur hotpink
avec les déclarations suivantes :
<p class="kiwi">Coucou !</p>
p {color: tomato;}
p {color: hotpink;}
Dans un monde idéal, l'intégration HTML / CSS est prise en compte en amont du projet, en se donnant les ressources adéquates et les compétences nécessaires. Ce n'est bien évidemment pas un domaine que l'on traite à la légère, après le budget alloué à la communication, au SEO, aux développements "lourds" etc. en se disant que "après tout ce n'est que du CSS, même pas un vrai langage".
Mais ça c'est dans un monde idéal.
Dans un vrai projet, on récupère du code produit par des anciens stagiaires disparus ou par des frameworks de version obsolète, on a totalement oublié de s'appuyer sur une convention de nommage, on intègre avec un onglet de Stackoverflow toujours ouvert dans un coin, et on peste contre le monde entier à tenter d'écraser des styles avec !important
(voire des !veryimportant
ah non ça c'était une blague).
Qui n'a jamais utilisé !important
me jette la première <br>
!
Le vrai projet, c'est celui où l'on passe toujours trop de temps à comprendre pourquoi nos paragraphes sont chocolate
et pas hotpink
dans ce cas là :
p {color: tomato;}
.kiwi {color: pink;}
div p:first-child {color: chocolate;}
p.kiwi {color: hotpink;}
Les spécifications CSS ont introduit une règle-at @layer
permettant de redéfinir l'ordre et la priorité dans la cascade CSS à l'aide de "layers" (couches).
Le principe général est simple : l'ordre de déclaration des layers (Layers Order) est prioritaire sur la spécificité des sélecteurs.
Ainsi, dans l'exemple qui suit, les paragraphes prendront la couleur hotpink
car leur couche est déclarée en dernier.
@layer reset {
p {color: olive;}
.kiwi {color: pink;}
div p:first-child {color: chocolate;}
}
@layer base {
p {color: hotpink;}
}
Il existe plusieurs moyens de créer des couches de styles : la règle @layer
avec styles associés, la même sans styles, ou l'import de fichiers de styles externes.
@layer
avec styles associésOn déclare la couche via @layer
en lui donnant un nom (optionnel) et on y applique des règles CSS.
@layer reset {
/* ici les règles CSS de la couche reset */
}
@layer base {
/* ici les règles CSS de la couche base */
}
@layer
sans stylesOn déclare la couche via @layer
vide.
@layer reset;
@layer base;
Pour info, cet exemple est équivalent à cette syntaxe :
@layer reset, base;
Cette formulation, sans styles associés, n'a d'autre but que de déclarer un ordre précis dans les couches.
La notion de layer peut être associée à la règle @import
:
@import url("reset.css") layer(reset);
En plus de l'import via @import
, le W3C travaille sur une version importée avec l'élément <link>
.
Concrètement cela représente un moyen très simple de pouvoir :
!important
… et si j'évoque ce framework en particulier, ce n'est pas tout à fait anodin (oui, il y a bien 1307 !important
dans ce seul fichier CSS)@import url("bootstrap.css") layer(framework);
Le postulat de base est que chaque layer est prioritaire sur le layer déclaré avant lui. L'ordre est donc primordial.
Dans l'exemple qui suit, tous les paragraphes seront de couleur hotpink
même s'ils disposent de la classe .kiwi
car le layer base
est déclaré après reset
:
@layer reset {
p.kiwi {color: tomato;}
}
@layer base {
p {color: hotpink;}
}
Il est possible d'ajouter des styles à une couche existante, simplement en reprenant le nom du layer déjà créé :
@layer reset {
p.kiwi {color: tomato;}
}
@layer base {
p {color: hotpink;}
h1 {color: olive;}
}
@layer reset {
h1 {color: chocolate;}
}
Dans cet exemple, les styles sur h1
sont ajoutés à ceux déjà présents dans la couche reset
.
Notez qu'étendre des layers ne modifie pas l'ordre originel (et donc l'application) des layers. En clair, ici la couleur des titres h1
sera olive
car le layer base
est déclaré après reset
.
Une bonne pratique consiste à définir dans un premier temps l'ordre de toutes les couches en une règle raccourcie (ex. @layer reset, base;
), puis d'étendre les styles de chacun des layers, ainsi il n'est pas possible de se tromper dans l'ordre d'application des couches.
Dans l'exemple suivant, je souhaite prioriser les styles de la couche base
, je vais donc commencer par indiquer l'ordre à respecter.
Ici, malgré ma maladresse (j'ai étendu les styles base
puis ceux de reset
), ce sont bien les styles de base
qui sont prioritaires et s'appliquent. Les paragraphes seront hotpink
:
@layer reset, base;
@layer base {
p {color: hotpink;}
}
@layer reset {
p {color: olive;}
.kiwi {color: tomato;}
}
Cela peut paraître curieux, mais les styles sans layer sont appliqués en priorité.
Ainsi, dans l'exemple qui suit les paragraphes seront de couleur hotpink
:
p {color: hotpink;}
@layer reset {
p {color: olive;}
p.kiwi {color: tomato;}
}
On peut imbriquer les Cascade Layers de cette manière :
@layer framework {
@layer reset {
}
}
Il est ensuite possible d'étendre les styles de ce layer en y faisant référence ainsi :
@layer framework.reset {
/* j'étends les styles dans le layer reset dans framework */
p { color: hotpink; }
}
Bonne nouvelle : CSS Cascade Layers est une spécification dont le support est relativement large. À l'heure où cet article est rédigé, seules les anciennes versions de Safari et Samsung Internet sont à la traîne (si on ne tient pas compte d'Internet Explorer bien sûr). Cela signifie que cette fonctionnalité peut très vite être utilisable en production.
Et vous, qu'en pensez-vous ? Êtes-vous aussi impatient que moi de pouvoir bénéficier de cette spécification afin d'assainir radicalement tous les anciens projets web que l'on maintient tant bien que mal ?
On l'a vu dans l'article "Pourquoi écrire du Twig dans WordPress ?", Timber est une excellente dépendance pour WordPress faite pour les développeurs.
J'aimerais faire le lien avec l'article "Préparer un thème WordPress pour l'internationalisation", car la syntaxe n'est pas forcément évidente si vous êtes habitués à l'écriture standard de WordPress.
Dans l'ensemble, rien de bien plus compliqué qu’avec PHP hormis la syntaxe. On utilise toujours la même fonction WordPress qui est aussi disponible dans Timber.
Voilà un exemple simple avec PHP :
<?php echo __( "Voir toutes les actualités", "textdomain" ); ?>
Et voilà son équivalent avec Timber :
{{ __( "Voir toutes les actualités", "textdomain" ) }}
Jusqu’ici tout va bien, si vous avez l’habitude d’écrire en Twig c’est la même chose.
Abordons maintenant un cas plus spécifique.
Dans cette partie, on utilisera l'exemple de la barre de recherche, qui est un composant que l'on retrouve fréquemment sur beaucoup de sites et qui est naturellement accompagnée d'une page de résultats, sur laquelle on retrouve une phrase récapitulant le nombre de résultats trouvés et la requête faite par l'utilisateur.
Cette dernière est souvent sous la forme : "X résultat(s) trouvé(s) pour le mot Y."
Ce qui induit qu'en fonction du nombre de résultats trouvés, cette phrase peut être au pluriel. Comme toujours, il y a plusieurs méthodes pour réaliser cela et je divulgâche, mais certaines de ces méthodes sont meilleures que d'autres sur plusieurs points.
En tant que développeur, on peut être tenté de gérer l'affichage avec une condition.
Par exemple :
sprintf
avec les spécificateurs adéquats respectivement pour le nombre de résultats et le terme de recherche.En PHP, cela donnerait :
<?php if( $search_result > 1 ) : ?>
<?php
sprintf(
__( "%d résultats trouvés pour le mot \"%s\".", "textdomain" ),
$search_result,
get_search_query()
);
?>
<?php else : ?>
<?php
sprintf(
__( "%d résultat trouvé pour le mot \"%s\".", "textdomain" ),
$search_result,
get_search_query()
);
?>
<?php endif; ?>
Et avec Timber :
{% if search_result|length > 1 %}
{{
__( "%d résultats trouvés pour le mot \"%s\".", "textdomain")|format(
search_result|length,
fn('get_search_query')
)
}}
{% else %}
{{
__( "%d résultat trouvé pour le mot \"%s\".", "textdomain" )|format(
search_result|length,
fn('get_search_query')
)
}}
{% endif %}
C’est la même chose, excepté que l’on remplace un appel de fonction par un filtre.
On y retrouve :
length
sur la variable search_result
renvoie le nombre d'éléments.__()
pour les traductions avec le bon textdomain
format
qui est l’équivalent de la fonction sprintf
en PHPfn()
ou function()
qui permet d’utiliser une fonction WordPress ou PHPAvec cette méthode, si search_result
est supérieur à 1, on obtiendra :
2 résultats trouvés pour le mot Burger
.
Sinon :
1 résultat trouvé pour le mot Burger
.
Parfait, le résultat est comme attendu. Ça fonctionne !
Dans notre outil de traduction (Poedit), on retrouve bien les chaînes de caractères à traduire. Cependant, il y a une chaîne pour gérer le singulier et une pour gérer le pluriel.
Pourquoi ça ne va pas ?!
Il existe des fonctions faites pour ce genre de cas !
Pour avoir des traductions singulier/pluriel propres et optimisées, on utilise la fonction WordPress _n()
, qui est un tout-en-un.
Il suffit de lui passer la valeur qui sert de référence et elle s'occupera de choisir d'afficher le singulier ou le pluriel.
Toujours dans l'exemple de page de résultats de recherche, voici comment l'utiliser avec PHP :
<?php
sprintf(
_n(
"%d résultat trouvé pour le mot \"%s\".",
"%d résultats trouvés pour le mot \"%s\".",
$search_result,
"textdomain"
),
$search_result,
get_search_query()
);
?>
Et son équivalence avec Timber :
{{
__(
_n(
"%d résultat trouvé pour le mot \"%s\".",
"%d résultats trouvés pour le mot \"%s\".",
search_result|length,
"textdomain"
)|format(
search_result|length,
fn('get_search_query')
)
)
}}
Mais c’est plus imbriqué, plus dur à lire/écrire et la finalité est la même ! Alors ça change quoi ? 🤔
Le résultat dans l'outil de traduction est complètement différent !
Pourquoi c’est mieux ? Pourquoi privilégier cette méthode ?
Et voilà ! Vous avez une bonne pratique supplémentaire à intégrer dès le début du développement de votre thème WordPress avec Timber !
À bientôt pour un prochain article. 🙂
Retrouvez l'intégralité de ce tutoriel en ligne sur Alsacreations.com