Je porte mon dévolu dans ce billet sur la propriété dataset des éléments d'une page web. C'est vraiment fou ce qu'on peut faire avec ça!
La force de la méthode forEach()
Pour boucler dans un lot d'éléments DOM, rien de mieux que d'utiliser .forEach(), et ainsi de remplacer la méthode jQuery .each().
"var" n'est pas obsolète, mais c'est tout comme
En JavaScript, le mot-clé var
était un mal nécessaire, avant 2015. C'était le seul mot-clé à utiliser pour restreindre la portée des variables. La norme ES2015 (ou ES6) a introduit par la suite const
et let
, qui sont aujourd'hui beaucoup plus logiques, tellement que cela rend var
quasiment inutile.
insertAdjacentHTML, alternative à jQuery append(), prepend(), before() et after()
Billet très court. Ce remplacement est assez facile à réaliser.
Le JavaScript doit rester du JavaScript
Le titre est peut-être vague. Je vais essayer de phraser ça autrement : Le JavaScript ne devrait jamais contenir d'éléments d'autres langages. Dans le cas contraire, cela devient difficile pour tout validateur de ce monde (jsLint, par exemple) de faire son travail. Et ça permet de gagner en robustesse et en fiabilité.
Avertir les gens du non-support de leur navigateur
C'est vrai, c'est banal, mais ça peut sauver beaucoup de temps de débogage : si on met du JavaScript dans notre site, c'est très important de savoir quels navigateurs on veut supporter.
fetch(), la suite : annuler les requêtes
Dans mon billet sur fetch(), j'avais laissé entendre que je ne comprenais pas tout de la méthode.
Remplacer slideDown() et slideUp() dans jQuery
Ce n'est pas facile de trouver une solution aisée à remplacer les méthodes $.slideDown() et $.slideUp(), qui servent essentiellement à plier et déplier des boîtes verticalement. Heureusement, Javascript a assez évolué pour pouvoir programmer une alternative assez succincte.
Éviter le mot-clé « this »
Le mot-clé this
1 est un raccourci parfois utile lorsqu'on écrit du nouveau code JavaScript. Ce mot-clé a cependant la particularité de se comporter différemment du this
en C, C++ ou Java. Il se comporte même différemment selon la version JavaScript ou même si on est en mode strict ou pas.
C'est pourquoi je déteste ce mot-clé. Je fais tout en mon possible pour éviter d'y avoir recours.
Mon argumentaire tient toujours à la maintenance du code. C'est à dire d'être capable de relire mon code six mois plus tard et le comprendre avec le moins d’ambiguïtés possibles.
Je me suis déjà retrouvé avec un code semblable:
/* Considérant <button id='allo'>Allo</button> */
const objet = {
f0 : 'allo',
f1 : function() {
document.getElementById(this.f0).addEventListener('click',objet.f2);
},
f2 : function() {
console.log(this.id)
}
}
objet.f1();
Si on exécute ce code, il va fonctionner, mais le this
dans f1() n'est pas le même this
que dans f2(). Dans f1(), this
réfère à la constante objet
, alors que dans f2(), this
réfère au bouton #allo
.
On pourrait faire la même action en évitant ce mot-clé maudit.
/* Considérant <button id='allo'>Allo</button> */
const objet = {
f0 : 'allo',
f1 : () => {
document.getElementById(objet.f0).addEventListener('click',objet.f2);
},
f2 : e => {
console.log(e.target.id)
}
}
objet.f1();
Ça fonctionne aussi. this
est éliminé. Le code est un peu plus compréhensible. Cependant, la méthode f2(), à moins qu'elle soit appelée ailleurs, devrait être déplacée à l'intérieur de f1(), question d'éviter qu'elle soit visible de l'extérieur de l'objet.
/* Considérant <button id='allo'>Allo</button> */
const objet = {
f0 : 'allo',
f1 : () => {
document.getElementById(objet.f0).addEventListener('click', e => {
console.log(e.target.id)
});
}
}
objet.f1();
Curieusement, ça fait même une méthode plus courte.
Ne pas hésiter à nommer les choses
Il est toujours mieux, pour le suivi et surtout le débogage, de nommer ce sur quoi on travaille. Je veux dire par là de créer des variables pour retenir sa définition.
/* Considérant <button id='allo'>Allo</button> */
document.getElementById('allo').addEventListener('click', e => {
console.log(e.target.id)
});
...est bien, et va écrire le mot « allo » dans la console, si on a l'assurance que le bouton ne contient que le mot Allo.
/* Considérant <button id='allo'><span>Allo</span></button> */
document.getElementById('allo').addEventListener('click', e => {
console.log(e.target.id)
});
... va sûrement planter, car e.target risque fort de pointer vers le span
plutôt que sur le button
.
const allo = document.getElementById('allo');
allo.addEventListener('click', () => {
console.log(allo.id)
});
... ne plantera plus. Non seulement il ne plantera plus, mais en plus, à la relecture, on comprend tout de suite ce qu'on veut faire afficher dans la console.
Je ferais même un petit ajout pour éviter des surprises plus tard:
const allo = document.getElementById('allo');
if (allo) allo.addEventListener('click', () => {
console.log(allo.id)
});
... comme ça, si jamais une modification plus tard ferait disparaître le bouton #allo
, ça ne fera pas planter le code.
jQuery doit mourir
Ok, le titre est un peu exagéré. Mais jQuery est devenu somme toute une tare dans la programmation JavaScript. Aujourd'hui, utiliser jQuery retarde l'apprentissage du JavaScript. jQuery demande le chargement d'une bibliothèque de plus de 200 kibioctets, ce qui peut paraître léger pour un ordinateur de bureau, mais très lourd pour un mobile. Et son utilisation, tout comme n'importe quelle bibliothèque, impose souvent ce constat : on charge du code qui ne sera jamais utilisé.
Ceci dit, je dois reconnaître les bons coups de jQuery, et sa grande pertinence au moment où il a été créé. Le projet est publié pour la première fois en janvier 2006, à un moment où Internet Explorer 7 n'était même pas encore sorti. Et Internet Explorer 6, rempli de bogues. Mozilla Firefox gagnait des parts de marché. Google Chrome n'existait pas. Bref, à ce moment-là jQuery facilitait l'utilisation de JavaScript, un langage qui n'était pas interprété de la même façon par les deux navigateurs concurrents, dont l'un avait été littéralement abandonné par son éditeur (IE6 était sorti en 2001). jQuery "patchait" les lacunes de l'un et même parfois celles de l'autre. jQuery permettait alors de coder du JavaScript sans se soucier si c'était du JavaScript IE ou du JavaScript Mozilla.
Depuis sa création, Google Chrome est apparu, utilisant le même moteur de rendu que KHTML, un navigateur sous Linux. Respectueux des standards du web, tout comme Mozilla Firefox, et agressif du côté de sa mise en marché, il est devenu le navigateur dominant, éclipsant Internet Explorer. Microsoft renomme IE en Edge, et finit par utiliser le même moteur de rendu que Chrome, étant devant le fait accompli qu'il ne pouvait pas suivre la cadence de l'évolution des standards, face à deux navigateurs concurrents utilisant des moteurs de rendu à code source ouvert. Depuis que jQuery est né, les téléphones cellulaires ont fait leur apparition, avec le premier iPhone en 2007, suivi d'Android peu de temps après, et qui a fait que la majorité de la navigation web se fait sur mobile que sur un ordinateur de bureau. Tous ces navigateurs tournent avec un moteur JavaScript respectant un standard ECMAScript. jQuery n'a ici plus besoin de "patcher".
Donc aujourd'hui, les navigateurs doivent exécuter du code JavaScript court et rapide à exécuter. L'utilisation de jQuery permettait de coder court, mais jQuery lui-même ne l'était vraiment pas. Et plus il y a de code JavaScript dans une page, plus la création d'objets jQuery (à force de faire des $() par ci, et des $() par là), devient lourde en mémoire. Et à mesure que le JavaScript évolue, avec l'apparition de querySelector()
et querySelectorAll()
, ainsi que de fetch()
et closest()
, plus le besoin de recourir à jQuery s'amenuise. Tant et si bien qu'en 2022, jQuery n'est carrément plus utile.
Non seulement, il n'est plus utile, mais il nuit à la compréhension pour le novice de ce qu'est le DOM. À force de vouloir encapsuler les éléments de la page web dans ses objets, jQuery crée une distance inutile pour traiter la page web.
Qui plus est, sur des sites en maintenance, il peut conduire à l'apparition de code mort. document.getElementById('serie').classList.add('fr')
est équivalent à $('#serie').addClass('fr')
. Mais si plus tard, on supprime du code HTML l'élément "#serie", le code jQuery continuera de s'exécuter sans aucun message d'erreur, alors qu'il aurait dû être supprimé aussi en même temps.
De plus, le code jQuery exécute une création d'objet jQuery contenant une liste d'objets sur laquelle il va itérer pour ajouter à tous les éléments la classe 'fr', alors que le programmeur sait très bien qu'il n'a pas de liste à impliquer dans son opération. En termes de rapidité, document.getElementById('serie')
est 25 fois plus rapide que $('#serie')
.
Voilà pourquoi en partie l'existence de ce blogue. Je veux faire connaître JavaScript dans ce qu'il a de plus pur, sans bibliothèques. Vous remarquerez alors à quel point le langage est rapide et puissant lorsqu'il est débarrassé de cossins voulant révolutionner ce qui est déjà révolutionnaire.
La beauté de la combinaison de addEventListener() et closest()
Pour remplacer la méthode jQuery.on()
, ou son ancêtre jQuery.live()
, qui permettent d'attacher un événement virtuellement à des éléments qui n'existent pas encore au moment de l'appel, ces deux méthodes sont très utiles.
EventTarget.addEventListener()
existe depuis longtemps. En fait, c'est Internet Explorer (encore) qui a été le dernier à l'intégrer, à sa version 9. Ça fait quand même une couple de lunes.
La méthode Element.closest()
, quant à elle, est un peu plus récente. Implémentée dans Chrome et Firefox au début de 2016 et dans Edge fin 2017, elle permet d'imiter quelque peu la méthode jQuery.parent()
lorsqu'elle contient un sélecteur CSS, à la différence suivante:
Considérant :
<div class='a'>
<div id='moi' class='b'><;/div>
</div>
document.getElementById('moi').closest('.b').className='b c'; // Ajoutera la classe 'c' dans div#moi
$('#moi').parent('.b').addClass('c'); // Ne fera rien.
Autrement dit, element.closest()
commence à chercher à partir de lui-même, alors que $(element).parent()
commence à chercher à partir de son premier parent.
addEventListener + closest = $.on()
Là où ça devient intéressant, c'est que la méthode jQuery.live()
, et son successeur $.on()
qui permettent d'attacher un événement à des éléments de la page qui peuvent apparaître ou disparaître en cours de route, devient plus facile à faire en javascript vanille.
Là où l'on utilisait:
$("body").on("click", "a", function(event) { event.preventDefault(); alert("Tu as cliqué sur un lien!") });
On peut maintenant utiliser, sans jQuery:
document.body.addEventListener("click", event => { const a = event.target.closest("a"); if (!a) return; event.preventDefault(); alert("Tu as cliqué sur un lien!") });
Javacript - La suppression des événements avec removeEventListener() au lieu de $.off()
L'affaire qui se passe, c'est que $(el).on()
ne fait pas seulement la même chose que el.addEventListener()
. $(el).on()
enregistre aussi l'événement dans une liste d'événements d'un certain type (ex:'click') pour que si jamais on fasse $.off()
sur le même type d'événement, ça puisse les effacer.
Une façon de faire pour effacer tous les événements possibles sur un élément serait el.outerHTML=el.outerHTML;
... sauf que ça supprime TOUS les événements de TOUS les types.
Idéalement, 2 trucs:
- Soit on nomme l'événement (autrement dit, une fonction qu'on retient dans une variable) :
var el = document.querySelector('textarea'); var foo = () => console.log('Allô!'); el.addEventListener('click', foo); // ajoute el.removeEventListener('click', foo); // enlève
- Soit on attache l'événement à son élément parent, au pire à document.body (live binding) :
document.body.addEventListener('click', e => { const el = e.target.closest('textarea'); if (!el) return; console.log('Allô!'); }
Remplacer $.get(), $.post(), $.ajax() et $.getJSON() par fetch()
Dieu merci, on a créé une méthode pour remplacer l'inutilement compliqué XMLHttpRequest. Par contre, fetch() retourne un type d'objet qui n'est pas toujours bien compris par les développeurs. Et je m'y inclus...
Mais bon, ça permet toujours bien de créer des appels XHR sans trop de code superflu.
Pour un appel avec la méthode GET, remplacer :
$.get(url,function(data){/*CODE*/});
par :
fetch(url).then(p => p.text()).then(data => {/*CODE*/});
Pour un appel avec la méthode GET qui est sensé retourner un objet JSON, remplacer :
$.getJSON(url, function (data) {/*CODE*/});
par :
fetch(url).then(p => p.json()).then(data => {/*CODE*/});
Pour un appel avec la méthode POST, remplacer :
$.post(url,{a:1,b:2},function(data){/*CODE*/};
par :
fetch(url,{method:'POST',body:"a=1&b=2"}).then(p=>p.text()). then(data => {/*CODE*/});
Et pour un POST retournant un JSON, réutiliser la méthode ci-dessus en changeant p.text()
par p.json()
.
Évidemment, j'y vais sommairement, parce que l'utilisation successive des méthodes .then() n'y est pas pour faire joli; cela permet surtout de trapper les exceptions, en ajoutant par exemple un .catch() à la toute fin, au cas où l'appel retournerait une erreur 404, un objet JSON cassé, ou je ne sais quoi d'autre.
La "sérialisation" des formulaires
C'est tellement facile avec jQuery, et jadis autrement plus compliqué en JS vanille. Heureusement, ça s'est (un peu) amélioré dans les dernières années, avec la venue de deux nouveaux types d'objets : FormData et URLSearchParams.
Normalement les deux expressions suivantes devraient donner le même résultat :
const r = $(form).serialize(); // ou const r = (new window.URLSearchParams(new FormData(form)).toString());
Je dis bien « devrait », mais ça se peut qu'il y ait des différences d'encodages. Bref, on ne peut pas remplacer une expression par l'autre en toute confiance sans tester.
Pourquoi ce blogue
Je suis programmeur informatique depuis 1995. Développeur web depuis 2005.
J'ai passé par plusieurs langages: Foxpro, Assembleur, Clipper, VisualBasic, ASP... et depuis maintenant 15 ans LAMP (Linux-Apache-MySQL-PHP). Et le javascript s'est insidieusement imposé dans mon bagage de connaissances - tu ne peux pas faire 15 ans de web sans avoir touché au javascript!
Au début, le langage me rebutait. C'était une bibitte (je suis Québécois, alors ça se peut qu'il y ait certains régionalismes qui m'échappent, désolé) avec laquelle je dois composer, parce que, maudit torrieux (décidément...), on n'est pas pour tout faire côté serveur.
Et tranquillement pas vite, on s'habitue. Puis, on adopte. Puis, on aime ça, finalement!
Sans être rendu à me convertir à Node JS, je constate la puissance du langage depuis quelques années, surtout depuis que les navigateurs se sont entendus pour fixer des normes (ES6 et suivantes).
Malheureusement, je constate aussi, que mis à part MDN, qui est en train de devenir une référence des technologies web, peu de blogues documentent les dernières avancées du langage, de surcroît en français. Je me surprends encore à utiliser des techniques qui ne sont plus utiles depuis les dernières années, où l'on a vu disparaître l'infâme navigateur Internet Explorer (ceux qui me connaissent depuis longtemps savent tout le fiel que j'y ai versé), celui-là même qui nécessitait l'utilisation de ces techniques.
J'ai senti là un besoin, besoin que je vais essayer bien humblement de combler pour mes compères programmeurs.
C'est un blogue dans les règles de l'art, avec un bloc de commentaires. Vous pouvez donc commenter mes billets, mais bien évidemment, restez dans le sujet...
Si vous avez des questions ou des suggestions de sujets pour ce blogue, vous pouvez m'écrire dans la page contact.
Bonne visite!