Je voudrais discuter de comment et pourquoi sortir du jQuery par défaut sur les projets web. Pour une fois je vais tenter d’écrire un article en français et court1.

Pour résumer mon opinion: il n’y a pas d’urgence à sortir jQuery de vos projets, et dans certains cas ça peut même être une mauvaise décision. Mais apprendre à faire sans, c’est un bon moyen pour mieux se familiariser avec JavaScript et les fonctionnalités natives des navigateurs, y compris les plus récentes (DOM4).

jQuery partout

W3Techs estime que jQuery est utilisé sur 70% des sites web. C’est gigantesque! En bientôt 10 ans depuis sa première version à l’été 2006, jQuery est devenu incontournable. Chez Kaliop, comme chez mes précédents employeurs et dans la plupart de mes projets en freelance, la question se pose rarement: on met la dernière version de jQuery — ou un fichier copié du dernier projet — vers le début de l’intégration et voilà.

La popularité de jQuery est largement méritée:

  • c’est un projet mature, stable et activement maintenu;
  • avec une API succinte et plutôt intuitive;
  • qui résoud aussi quelques problèmes de compatibilité.

Le mécanisme de plugins a aussi fortement contribué à son succès.

Pour autant, jQuery n’est pas magique. Tout ce que fait jQuery, vous pouvez le faire directement en utilisant les APIs natives du navigateur, en particulier le DOM. Quelques exemples pas strictement équivalents mais proches:

// Avec jQuery
var header = jQuery('.header');
header.on('click', function(event){ … });

// DOM natif
var header = document.querySelector('.header');
header.addEventListener('click', function(event){ … });

Petite pause vocabulaire

Dans cet article je parle de JavaScript, de jQuery, de DOM et d’API, alors on va essayer de définir tout ça viteuf, parce que c’est pas pareil.

JavaScript: un langage de programmation, qui est le langage de programmation utilisable dans un navigateur Web2. JavaScript a une syntaxe, avec des variables, des fonctions, des objets, etc. Avec cette syntaxe on peut faire deux-trois trucs, mais dès qu’on veut sortir de la boite où tourne le moteur JavaScript, par exemple pour modifier des éléments sur la page ou envoyer une requête HTTP, il faut utiliser des… APIs.

API: pour Application Programming Interface, ce qui se traduit en français par «ensemble de fonctions pour faire des trucs avec des machins». Dans les navigateurs on trouve plein d’APIs pour faire plein de trucs avec plein de machins (des requêtes HTTP, des connexions en peer-to-peer, modifier des flux audio pour faire un synthétiseur dans ton navigateur, etc.). Et pour utiliser ces APIs on écrit du code en… JavaScript.

DOM: le Document Object Model c’est un ensemble de spécifications qui décrivent une API (eh oui) qui permet de faire des trucs avec un document HTML ou XML. Par exemple «lire» le contenu de ce document, écrire dedans, créer des éléments HTML, les injecter dans la page, modifier des attributs, etc. JavaScript tout seul ne peut pas faire ça, il a besoin du DOM. Ah oui, et dans les spécifications du DOM il y a aussi DOM Events, qui décrit un mécanisme d’évènements («l’utilisateur clique quelque part dans la page, paf ça fait un évènement, tu peux écouter cet évènement et y réagir si tu veux»).

jQuery: c’est un gros script écrit en JavaScript et qui fournit une API (la doc est sur api.jquery.com, d’ailleurs) pour faire des trucs avec un document HTML. Plus deux ou trois autres fonctionnalités utiles, comme des utilitaires pour faire des requêtes HTTP (vous connaissez peut-être sous le nom de «Ajax», sans lien de parenté avec le héros grec ou la lessive).

Et un peu d’histoire

Là comme ça, on dirait que jQuery fait la même chose que le DOM, et donc que ses créateurs se sont emmerdés à réinventer la roue. En fait ça s’est passé un peu différemment.

À l’époque de la création de jQuery, le DOM avait un nombre de fonctionnalités limitées et était, pour tout dire, assez chiant à utiliser. Il fallait écrire beaucoup de lignes de code pour faire pas grand chose. Sans parler des trucs possibles mais trop difficiles à faire.

Dev: Salut le DOM, je peux avoir tous les éléments qui ont la classe «machin»? DOM: Hmmm c’est compliqué. Dev: Mais c’est possible ou pas? DOM: Oui mais il faut faire ça d’abord, et puis ça, et enfin ça. Dev: Punaise… et sinon pour ajouter un élément juste après celui-ci, je fais comment? DOM: Tu veux pas le mettre avant, plutôt? Dev: Euh non je voudrais le mettre après. DOM: Moi j’ai juste appris à mettre des éléments avant, je sais pas les mettre après. C’est peut-être faisable en trois étapes, faut voir.

Pour corser le tout, les spécifications du DOM n’étaient pas complètement implémentées dans les navigateurs, et notamment dans Internet Explorer qui faisait une partie des trucs à sa sauce. Donc il fallait parfois écrire des fonctions qui faisent une chose dans Internet Explorer, une autre dans les autres navigateurs, pour obtenir le même résultat final.

Les projets comme Prototype, MooTools et jQuery avaient donc pour but de corriger ces différences en proposant une seule manière de faire (la leur), et aussi de faciliter l’utilisation du DOM.

Dev: jQuery, jpeux avoir les éléments avec la classe «machin»? jQuery: Bien sûr, les voilà. Dev: Cool. Et pour ajouter un élément juste après celui-ci, ça se fait en 3 étapes c’est ça? jQuery: Ouais mais je m’en occupe, tkt.

Mais alors, pourquoi faire sans?

Il y a quand même quelques bonnes raisons de se passer de jQuery, en particulier sur de petits projets qui n’ont pas des dizaines ou centaines de fonctionnalités JavaScript.

Parce que ça pèse lourd, mine de rien

Je me souviens des premières versions de jQuery, qui faisaient dans les 50 Ko avec minification. Aujourd’hui, jQuery 1.12 fait 97 Ko minifié, et jQuery 2.2 fait 85 Ko (idem pour jQuery 3.0).

Ça n’est pas excessif vu les fonctionnalités apportées, mais encore faut-il en faire réellement usage. Si c’est pour écrire 100 lignes de code maison à tout casser, ça commence à couter cher. Et comme il s’agit de JavaScript, il faut aussi compter le temps d’analyse et d’exécution de ce code (il n’est pas anodin, en particulier sur des mobiles peu performants).

Parce que jQuery bloque l’apprentissage

C’est le plus gros problème que je vois avec jQuery. Beaucoup de gens qui ont besoin de faire du JavaScript dans leur métier ne savent pas coder en JavaScript, et ne connaissent pas le DOM; iels3 ont appris à faire du jQuery (et pas toujours bien). Quand iels se retrouvent sur un projet sans jQuery, iels sont bloqués.

Si vous connaissez jQuery mais que vous ne savez pas expliquer la différence entre ces trois variables:

var test1 = $('.test');
var test2 = $('.test')[0];
var test3 = document.querySelector('.test');

… alors il vous manque la compréhension de concepts essentiels concernant jQuery, le DOM, et même la syntaxe JavaScript. Ce n’est pas un reproche, mais ça vous limite.

De plus, jQuery n’est pas une abstraction parfaite au dessus du DOM, qui permettrait de s’en passer totalement. Il y a plusieurs cas de figure où des méthodes de jQuery exposent des éléments du DOM plutôt que des objets jQuery:

var items = $('.something li'); // objet jQuery
items.on('click', function(event) {
    console.log(this); // objet HTMLLIElement
});
items.each(function(index, element){
    console.log(element); // objet HTMLLIElement
});

Pour pouvoir continuer à travailler uniquement avec les méthodes de jQuery, il faut donc re-fabriquer un objet jQuery avec ces éléments:

$('.something li').each(function(index, element){
    element = $(element);
});

Et comme les développeurs/ses3 ne savent souvent pas quelle est la différence exacte entre un objet jQuery et un objet du DOM, iels se retrouvent souvent avec des messages d’erreur du type TypeError: element.attr is not a function, et rajoutent des $() autour de leurs variables à tour de bras, même quand ce n’est pas du tout nécessaire.

Mais produire du code JavaScript+jQuery mal optimisé, ce n’est pas si grave. Le principal problème à mon sens c’est qu’on en arrive à ne pas savoir faire sans jQuery, à ne jamais avoir appris les bases du DOM ou des bases de JavaScript un peu solides. Avec quelques conséquences:

  1. Si vous avez écrit du code théoriquement réutilisable sur d’autres projets, mais que ces projets n’utilisent pas jQuery… votre code n’est pas réutilisable.
  2. Si un projet n’utilise pas jQuery, vous n’êtes plus capable d’intervenir dessus.

Petite anecdote: un projet en mode maintenance, datant d’il y a quelques années, utilisait MooTools (un concurrent de jQuery). Un-e développeur/se junior à qui on avait confié la réalisation d’une nouvelle fonctionnalité ne savait pas faire sans jQuery. Iel a donc rajouté jQuery (non minifié, sinon c’est moins drôle) dans le projet, et codé la fonctionnalité JavaScript demandée. Avec pour conséquence: 250 Ko de plus chargé par page, et toutes les autres fonctionnalités JS du site plantées à cause de conflits entre jQuery et MooTools.

Parce que le DOM natif s’est quand même bien amélioré

D’une part, IE8 puis IE9 ont enfin corrigé les grosses différences d’implémentation des standards (la création de gestionnaires d’évènements avec addEventListener par exemple).

D’autre part, voyant que personne n’utilisait le DOM directement car c’est trop relou, quelques améliorations bienvenues sont apparues dans les spécifications DOM:

  • document.querySelector et document.querySelectorAll (IE8+) pour sélectionner un ou plusieurs éléments.
  • element.insertAdjacentHTML (qui date de IE4 et a été standardisé ensuite).
  • element.classList (IE10+) qui permet d’ajouter et supprimer des classes facilement.
  • Et dans les spécifications DOM récentes (parfois appelées “DOM4”), les méthodes prepend, append, before, after, replaceWith, remove; document.query et document.queryAll qui améliorent quelques aspects de querySelector[All]; element.matches, element.contains, element.closest qui ressemblent beaucoup à certaines méthodes de jQuery.

Le dernier lot d’améliorations (“DOM4”) est assez récent et pas encore implémenté partout, mais il existe des polyfills plutôt légers.

Ces améliorations rendent envisageables l’utilisation de l’API DOM standard sans passer par un utilitaire un peu lourd comme jQuery (et sans s’arracher les cheveux).

Pour manipuler le DOM le plus tôt possible

Pour certaines fonctionnalités JS, mieux vaut pouvoir modifier le DOM quasi-instantanément, plutôt qu’attendre le chargement d’un gros fichier externe contenant jQuery, d’autres libs et vos scripts.

Sur certains projets, nous avons par exemple des petits scripts qui font 1, 2 ou 3 Ko minifiés, et qui sont inclus intégralement juste après les contenus HTML, et avant l’appel de jQuery ou d’autres scripts.

Il ne faut pas en abuser, mais ça peut être un bon moyen pour optimiser les performances perçues par l’utilisateur, ou éviter les demandes de clients qui trouvent que tel ou tel contenu affiché intégralement le temps que JS charge c’est innacceptable.

Pour essayer!

Si vous n’avez jamais codé sans jQuery, ou si vous l’avez fait uniquement au milieu des années 2000, ça vaut le coup d’essayer. Vous apprendrez au passage le fonctionnement du DOM natif, améliorerez sans doute votre compréhension de la syntaxe JavaScript, et comprendrez mieux ce que fait jQuery.

Sans les m… euh sans jQuery

Bon alors, comment faire pour se passer de jQuery sur un projet? Voici quelques approches intéressantes.

Le DOM natif compatible IE10+

Si vous pouvez laisser de côté le support d’IE9, pas mal de fonctionnalités DOM natives sont disponibles sans avoir à charger de lib ou de polyfill. Notamment: addEventListener, querySelector, et classList. Avec les fonctionnalités DOM plus classiques, setAttribute appendChild, innerHTML, textContent et insertAdjacentHTML, vous avez de quoi manipuler des éléments dans la page sans trop de souci.

Sur un tout petit projet, je vous recommande vraiment cette approche, qui vous aidera à consolider vos connaissances.

DOM4 avec un polyfill

Si vos besoins dépassent les choses les plus simples, les méthodes du standard DOM4 permettent de se rapprocher un peu de la facilité d’utilisation de jQuery et consors.

Deux exemples avec les méthodes DOM4:

// Contrairement à querySelectorAll qui retourne une NodeList,
// queryAll retourne un Array, ce qui facilite les boucles
document.queryAll('nav a').forEach(function(element) {
  // Faire quelque chose avec chaque lien
})

// element.closest('a') retourne l’élément lui-même si c’est
// un lien, ou son plus proche ancêtre qui est un lien. Cela
// permet de faire de la délégation d’évènement à la mano.
document.query('nav').addEventListener('click', function(ev) {
  var link = ev.target.closest('a')
  if (link) {
    // Faire quelque chose avec le lien cliqué
  }
})

Pour utiliser ces méthodes dans tous les navigateurs, on pourra se reposer sur ce polyfill (IE9+ et 10 Ko minifiés, pas mal du tout).

DOM4 et un peu plus avec Bliss

Bliss, créé par Lea Verou, est une alternative à jQuery qui repose sur les méthodes natives du DOM et quelques polyfills pour les navigateurs qui en ont besoin. Ce projet s’inspire parfois de jQuery pour certaines méthodes, mais dès que possible il conseille d’utiliser la syntaxe DOM native. C’est donc un beau projet pour apprendre le DOM moderne.

De plus lorqu’on consulte la documentation on peut cliquer les boutons Show Implementation pour voir le code correspondant à une méthode.

Les jQuery-like

Certaines libs réimplémentent l’API de jQuery en plus léger, en général en visant seulement la compatibilité avec les navigateurs récents. Par exemple Zepto.js, qui est un des projets les plus complets, vise IE10+. La syntaxe proposée est exactement la même que celle de jQuery, car c’est un projet créé pour remplacer jQuery complètement sur mobile. Certains plugins jQuery sont d’ailleurs testés pour fonctionner également avec Zepto.

Ce n’est pas une approche que je recommande, car elle pose les mêmes problèmes de limitation dans l’apprentissage du JavaScript et du DOM.

Une note sur les requêtes HTTP

Alors on va arrêter de parler d’«Ajax» parce que ce nom est stupide, et que si vous faites du code pour le Web il est temps d’apprendre à connaitre les bases de HTTP, ce qu’est une requête HTTP et une réponse HTTP. Utilisez l’onglet «Réseau» de vos devtools préférés et inspectez un peu quelques requêtes et réponses pour vous familiariser avec tout ça.

L’API classique pour faire des requêtes HTTP en JavaScript se nomme XMLHttpRequest, et vous pouvez en apprendre plus sur les requêtes HTTP en JavaScript par ici (en anglais, car je n’ai pas trouvé d’article correct en français).

jQuery fournit des méthodes pour faire des requêtes HTTP, nommées jQuery.ajax(), jQuery.get() et jQuery.getJSON(). Donc si on se passe de jQuery, il faut soit utiliser XMLHttpRequest soi-même, soit utiliser un petit utilitaire qui facilite son utilisation.

Si vous choisissez Bliss ou une autre lib légère concurrente de jQuery, vérifiez ce qu’elle propose pour les requêtes HTTP (Bliss a une méthode Bliss.fetch, par exemple).

Autre option intéressante: utiliser le nouveau standard fetch() (Can I Use), avec un polyfill tel que github/fetch (IE10+).

Mini retour d’expérience

Sur un projet récent, j’ai donc fait uniquement du JavaScript sans jQuery. J’ai utilisé:

  • WebReflection/dom4, tip top.
  • github/fetch, ce qui marchait très bien mais que j’ai fini par enlever car j’ai pu me passer des requêtes HTTP en JavaScript.
  • lunr.js pour de la recherche dans des données JS/JSON.

Les fonctionnalités JS à implémenter étaient:

  • Une navigation mobile à déplier au clic (sur 2 niveaux).
  • Une sorte de slider, que j’ai implémenté en ajoutant/supprimant des attributs hidden (pas d’effet d’animation, on affiche un bloc à la fois et tous les autres sont en display:none).
  • Une recherche full-text dans un gros fichier JSON. Ça a été la partie la plus compliquée du projet en JavaScript, et sur ce point jQuery n’aurait pas été d’une grande aide. La lib utilisée, lunr.js, est très bien, mais plutôt bas niveau donc il faut un peu de temps pour la comprendre et pas mal de code maison pour l’utiliser.

  1. Perdu. 😁 

  2. Certains ont bien essayé de mettre d’autres langages de programmation dans le navigateur (Microsoft avec VBSCript, Google avec Dart), mais ça n’a pas pris. Sans parler des plugins qui permettaient de faire du Java (petit succès) ou de l’ActionScript (gros succès) et qui sont passés de mode. 

  3. Autant que possible, cet article est écrit sans présupposer du genre des personnes même fictives mentionnées. Ainsi «développeur/se» est un raccourci pour «développeur ou développeuse» et le pronom «iels» est un néologisme qui mélange volontairement «ils» et «elles». Je suis consciente que ces mots et graphies ne facilitent pas la lecture pour celleux qui n’en ont pas l’habitude, mais ça compte pour moi donc osef. 😇