J'utilise JQuery depuis de nombreuses années, à tel point qu'il est devenu pour moi une évidence. Mais je lis de plus en plus d'articles qui exposent le fait que les navigateurs modernes comportent des API qui peuvent faire aussi bien, sinon mieux que JQuery. Alors il serait ridicule de ne pas creuser cette affaire pour voir exactement ce qu'il en est. Il faut parfois changer ses habitudes !
Les deux grandes capacités de JQuery résident dans la sélection et la modification des éléments du DOM et dans les requêtes Ajax. Il semblerait qu'aujourd'hui ces deux grands chapitres puissent être parfaitement assumés par les API, aussi bien au niveau des sélecteurs que d'Ajax avec Fetch.
Alors dans cet article je vous propose de faire un petit point sur le sujet. Je ne serai évidemment pas exhaustif parce que le sujet est très vaste et que pour le traiter complètement il faudrait écrire un bouquin !
Sélection simple
Vous avez sans doute tapé un grand nombre de fois du code du genre $('#item') ou $('.maclasse'). Eh bien on n'a vraiment pas besoin de JQuery pour ce genre de sélection !
Trouver un ID
Considérons ce code :<div id="monID"></div>
Avec JQuery on utilise :
var r = $('#monID');
En utilisant l'API on peut écrire depuis déjà longtemps :
var r = document.getElementById('#monID');
Pour mémoire l'interface Document représente une page web et est en même temps le point d'entrée du DOM. Dans un document la classe la plus générale est Element. La méthode getElementById renvoie justement un élément du DOM (puisque par principe un id est unique dans un document) ou null si l'id n'existe pas dans le document. L'avantage de cette méthode c'est qu'elle fonctionne sur tous les navigateurs, même les plus antiques.
Mais il y a une autre façon d'accomplir la même tâche avec la méthode querySelector qui fonctionne sur tous les navigateurs modernes :var r = document.querySelector('#monID');
On va voir après que cette méthode est beaucoup plus générale que getElementById.
Trouver une classe
Considérons ce code :<div class="maClasse"></div>
Avec JQuery on utilise :
var r = $('.maClasse');
En utilisant l'API on peut écrire depuis déjà longtemps ce code avec la méthode getElementsByClassName :
var r = document.getElementsByClassName('maClasse');
On retrouve dans la variable un tableau de tous les éléments qui ont cette classe. On peut aussi utiliser la méthode sur un élément particulier plutôt que document pour une recherche plus ciblée.
Mais il y a une autre façon d'accomplir la même tâche qui fonctionne sur tous les navigateurs modernes avec la méthode querySelectorAll :
var r = document.querySelectorAll('.maClasse');
Cette méthode renvoie un objet NodeList qui est une collection des éléments qui ont la classe concernée.
Rien n'empêche d'utiliser querySelector pour trouver une classe mais alors seul le premier élément sera renvoyé.
Trouver une balise
Considérons ce code :<ul id="maListe">
<li>Pain</li>
<li>Fromage
<ul>
<li>Camembert</li>
<li>Gruyère</li>
</ul>
</li>
</ul>
Pour trouver toutes les balises li dans la balise ul avec JQuery on utilise :
var r = $('#maListe li');
En utilisant l'API on peut écrire depuis déjà longtemps avec la méthode getElementsByTagName :
var liste = document.getElementById('#maListe');
var r = liste.getElementsByTagName('li');
On retrouve dans la variable un tableau de tous les éléments avec cette balise.
La liste retournée se met à jour automatiquement en cas de modification du DOM !
Mais il y a une autre façon d'accomplir la même tâche qui fonctionne sur tous les navigateurs modernes :var r = document.querySelectorAll('#maListe li');
Cette méthode renvoie un objet NodeList qui est une collection des éléments avec la balise concernée.
On peut utiliser la méthode querySelectorAll directement sur le document pour chercher sur toute la page web
Contrairement à getElementsByTagName la liste retournée est statique et ne tient pas compte des modifications ultérieures du DOM.Une histoire de famille
Voyons maintenant des sélections un peu plus complexes.Les ancêtres
Avec JQuery la recherche dans la hiérarchie du DOM est facile. Pour remonter dans les ancêtres on dispose des méthodes parent, parents, parentUntill...
Alors est-ce aussi facile avec l'API ? Voici un code html :<h1>Titre</h1>
<h2>Sous-titre</h2>
<p>Un paragraphe</p>
<div>
<p id="maListe>Une liste</p>
<ul>
<li class="un">un</li>
<li class="deux">deux</li>
<li class="trois">trois</li>
</ul>
</div>
Si on veut par exemple sélectionner le parent des balises li, donc la balise ul avec JQuery (mon exemple là est un peu idiot mais il suffira pour l'illustration) :
$('li').parent()
Avec l'API on utilise ce code avec la méthode parentNode :
document.querySelector('li').parentNode
Il existe aussi la méthode parentElement plus générale parce qu'elle renvoie non pas un node mais un element.
Si on veut tous les parents avec JQuery on écrit :$('li').parents()
Il n'existe pas de méthode équivalente dans l'API. Mais en a-t-on vraiment besoin ? Dans ce cas on peut toujours boucler avec parentNode ou parentElement.
JQuery propose aussi la méthode closest qui renvoie l'ancêtre le plus proche, l'API propose exactement la même méthode.
La descendance
Avec JQuery on peut trouver les enfants avec la méthode children. Par exemple avec :$('ul').children()
On va récupérer tous les li de la liste.
Avec l'API on a aussi une méthode children :
document.querySelector('ul').children
On obtient une collection des éléments enfants.
Les collatéraux
Avec JQuery on dispose des méthodes siblings, next et prev. Avec ce code :$('.un').next()
On obtient le li qui suit le premier. Et avec :
$('.un').siblings()
On obtient tous les autres li.
Avec l'API on n'a pas d'équivalent mais est-ce vraiment utile ? On peut s'en sortir directement avec les sélecteur CSS3. Par exemple pour remplacer le next :
document.querySelector('.un + li')
Et pour remplacer le siblings :
document.querySelectorAll('ul > li + li')
Si vous avez quelques lacunes avec les sélecteurs CSS3 j'ai créé une page interactive qui les illustre.
Les attributs
Les attributs permettent d'ajouter des informations aux balises, par exemple le nom d'un classe pour ajouter du style. JQuery sait bien gérer les attributs aussi bien au niveau de la sélection que de la modification. En est-il de même avec l'API ?
Avec jQuery on peut écrire :$('li[class="un"]')
On obtient ainsi la balise li qui comporte l'attribut class avec la valeur un. En fait on se contente d'utiliser une sélection CSS classique, du coup on peut faire la même chose avec l'API :
document.querySelector('li[class="un"]')
Les classes
Pour savoir si une balise contient une classe avec JQuery on écrit :$('li:first-child').hasClass('un')
Et avec l'API ? On dispose de la méthode className :
document.querySelector('li:first-child').className
On peut ensuite vérifier si une classe particulière est présente. Mais on a une autre solution avec classList qui renvoie une collection :
document.querySelector('li:first-child').classList.contains('un')
Mais cette méthode classList nous offre des possibilités intéressantes pour supprimer, ajouter, remplacer ou permuter une classe !
Par exemple avec JQuery on peut écrire :
$('li:first-child').removeClass('un').addClass('deux')
Avec l'API ça donne :
var r = document.querySelector('li:first-child')
r.classList.remove('un')
r.classList.add('deux')
Mais on peut aussi écrire en plus simple :
document.querySelector('li:first-child').classList.replace('un', 'deux')
Les autres attributs
Avec JQuery on peut savoir connaître la valeur de n'importe quel attribut :$('div > p').attr('id')
Là on obtient la valeur maListe. Avec l'API on peut utiliser la méthode getAttribute :
document.querySelector('div > p').getAttribute('id')
On dispose même de la méthode hasAttribute qui nous renseigne si un attribut est présent, ce qui à ma connaissance n'existe pas avec JQuery.
Pour changer la valeur d'un attribut avec JQuery on écrit :
$('div > p').attr('id', 'nouvelleListe')
Avec l'API on utilise setAttribute :
document.querySelector('div > p').setAttribute('id', 'nouvelleListe')
Les styles
Avec JQuery on peut facilement lire ou écrire les propriétés CSS. Par exemple :$('#maListe').css('color', 'red')
Avec l'API c'est aussi simple :
document.querySelector('#maListe').style.color = 'red'
Maintenant on peut quand même se poser une question quant aux bonnes pratiques. Il serait quand même plus judicieux de séparer les concepts, en l’occurrence la structure et sa présentation. Dans notre cas avoir un fichier CSS distinct et déclarer une classe sur une balise serait sans doute plus pertinent.
Un action très fréquente avec JQuery est aussi la facilité à rendre visible ou invisible un élément avec hide et show :$('#maListe').hide()
On peut même régler la vitesse du changement avec un second paramètre.
Avec l'API on peut évidemment utiliser la propriété display :
document.querySelector('#maListe').style.display = 'none'
Ça va fonctionner presque à tous les coups. Pourquoi presque ? Parce qu'avec le CSS on a une hiérarchie de règles et on est jamais vraiment sûr de son coup. Il vaut mieux utiliser la propriété hidden :
document.querySelector('#maListe').hidden = true
Modifier le DOM
Une force de JQuery est de rendre vraiment simple la manipulation du DOM. En est-il autant avec l'API ? Prenons un exemple en insérant une nouvelle balise li en tête de la liste :$('<li>en premier</li>').insertBefore('.un')
Avec l'API on dispose aussi d'une méthode insertBefore mais il faut commencer par un peu d'intendance :
var newLI = document.createElement('li')
var newContent = document.createTextNode('en premier')
newLI.appendChild(newContent);
var parent = document.querySelector('ul')
var r = parent.insertBefore(newLI, parent.firstChild)
C'est sûr que ça devient plus verbeux que la version JQuery ! Il faut détailler toutes les opérations :
- créer l'élément,
- créer son contenu,
- attacher le contenu à l'élément...
Au passage j'ai utilisé la méthode appenChild qui a pour mission d'ajouter un élément à la fin de la liste des enfants du parent désigné, en gros c'est l'inverse de insertBefore.
Pour supprimer un élément du DOM avec JQuery on écrit :$('#maListe').remove()
Avec l'API on utilise removeChild :
var element = document.querySelector('#maListe')
element.parentNode.removeChild(element)
Ici j'utilise parentNode pour trouver le nœud parent. C'est pratique quand on a seulement la référence de l'enfant.
Pour remplacer un élément par un autre avec JQuery on écrit :
$('#maListe').replaceWith('<h3>Une liste</h3>')
C'est simple et concis. Avec l'API on a aussi une méthode replaceWith mais c'est un peu plus long à écrire :
var e = document.createElement('h3')
var c = document.createTextNode('Une liste')
e.appendChild(c)
document.querySelector('#maListe').replaceWith(e)
Les événements
Avec Javascript on sait depuis longtemps gérer les événements même si JQuery se montre encore plus simple et concis dans le codage :
$('p').click(function(){ alert("J'ai cliqué sur un paragraphe") });
Avec l'API on utilise addEventListener :
function message() { alert("J'ai cliqué sur un paragraphe") }
document.querySelector('p').addEventListener("click", message)
Bon là mon code n'est pas tout à fait le même que celui avec JQuery parce que je ne considère que la première balise p de la page, mais pour le principe c'est suffisant.
On peut aussi retirer l'événement avec la méthode removeEventListener. Au passage on peut se demander par quoi remplacer le classique :$(document).ready(function() {
// code
})
qui permet d'être sûr que la page est chargée avant d'intervenir dessus. Avec l'API on dispose de l'événement DOMContentLoaded :
window.addEventListener('DOMContentLoaded', (e) => {
// On est sûr que la page est chargée !
})
Ajax avec fetch
S'il est un domaine où j'utilise intensément JQuery c'est bien pour les requête Ajax. Pourtant les navigateurs modernes (à part IE) autorisent l'utilisation de l'API Fetch qui est bien plus séduisant que XMLHttpRequest. Cet API permet de gérer les requêtes et les réponses, quelle qu'elles soient.
Basiquement on peut utiliser fetch ainsi :fetch('https://quelquepart.com/data')
.then( // code quand on a la réponse )
.catch( // code quand on a une erreur )
On voit que fetch utilise une promesse résolue quand la réponse arrive... ou pas...
La réponse reçue est en fait un objet response qui comporte de nombreuses propriétés comme par exemple le statut de la réponse :
fetch('https://quelquepart/data').then(function(response) {
if(response.ok) {
// Tout s'est bien passé
} else {
// Il y a eu un problème
alert(response.statusText)
}
})
.catch(function(error) {
alert('Il y a eu un problème : ' + error.message);
});
Je n'ai encore jamais utilisé cette interface mais elle semble riche et plutôt bien foutue. Prenons un exemple pour utiliser cet API. J'ai déjà parlé de l'API geo du gouvernement pour mon exemple de site d'annonces. Voyons comment récupérer les régions à partir de cet API avec fetch :
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Les régions de France</title>
</head>
<body>
<h1>Les régions de France</h1>
<ul></ul>
<script>
window.addEventListener('DOMContentLoaded', (e) => {
fetch('https://geo.api.gouv.fr/regions')
.then(response => {
if (response.ok) {
response.json().then(regions => {
const ul = document.querySelector('ul');
for (const r of regions) {
let newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(r.nom));
ul.appendChild(newLI);
}
})
} else {
console.error('Réponse du serveur : ' + response.status);
}
});
});
</script>
</body>
</html>
Au chargement on obtient :
Il faut un peu apprivoiser les promesses pour bien utiliser fetch !
Conclusion
Je pourrais encore évoquer certains points comme le data storage ou la gestion des dates. Mais je voulais juste montrer à partir de quelques exemples qu'on peut très bien désormais se passer de JQuery pour la plupart des tâches.
Toutefois il exitse une multitude de plugins pour JQuery dont il serait dommage de se passer. d'autre part des nombreuses librairies l'utilise comme par exemple Bootstrap. Mais il faut peut-être voir certains signes comme le fait que Materialize ne dépend plus désormais de JQuery pour ses plugins Javascript.
Par bestmomo
Nombre de commentaires : 2