Utilisation avancée des sélecteurs JQuery
Après avoir utilisé longtemps Prototype et Scriptaculous qui forment le framework Javascript par défaut dans Ruby on Rails, les sélecteurs de JQuery m’ont convaincu de passer à ce dernier (enfin, ça et la possibilité de ne charger qu’une partie du DOM de la réponse dans les requêtes ajax replace).
Dans un article récent faisant partie d’un ensemble d’articles d’initiation à JQuery, Dave Lizotte à fait une présentation rapide des sélecteurs. J’y reviens maintenant pour présenter les fonctionnalités avancées.
Le DOM, c’est quoi?
Au préalable, il est important que vous ayez saisi ce qu’est le DOM, car c’est sur lui qu’agissent les sélecteurs. Je ne vais pas réécrire ici la documentation du w3c, sachez simplement, si vous êtes étranger à cette notion, qu’il s’agit d’une structure représentant votre fichier HTML.
Imaginez-vous une vue en arborescence, avec l’élément html à la racine, et en enfants, les éléments head et body. En dépliant encore, vous trouverez les différents éléments enfants, par exemple un div dans le body, puis un p dans ce div, etc. Pour chacun, vous aurez accès à des attributs (title, id, class, etc) et à un contenu (le innerHTML).
Il vous suffit en fait de vous représenter ça, vous n’avez rien à apprendre. Le DOM n’est pas un langage, juste une interface. Au final, vous aurez l’impression d’agir directement sur le code HTML, et c’est ce que vous ferez, mais par l’intermédiaire du DOM.
Si vous voulez vous représenter cela plus visuellement et vous familiariser à la manipulation du DOM, je vous conseille l’excellente extension firefox qu’on ne présente plus : firebug.
On sélectionne quoi, au fait?
Les sélecteurs de JQuery servent à isoler des éléments du DOM. C’est en fait ce que font la plupart du temps les frameworks Javascript : modifier les éléments du DOM pour changer dynamiquement la page. Pour pouvoir faire cela, il est nécessaire de pouvoir cibler précisément un élément en particulier, ou un groupe d’éléments. C’est le travail du sélecteur.
Notez tout de suite qu’un sélecteur ne renvoie pas nécessairement un unique élément. Si vous utilisez le sélecteur $( ‘p’ ), vous obtiendrez en retour tous les <p> de la page. Il est possible d’itérer sur chacun grâce à la fonction each, mais si vous désirez un élément précis, soyez vous-même le plus précis possible dans votre code (la manière la plus simple étant de sélectionner un élément à partir de son id, comme par exemple $( ‘#footer’ ) ).
On avait pas dit : “avancé” ?
Si. Que ceux qui connaissaient déjà tout cela m’excusent, ce préambule me semblait indispensable pour être sûr que ce soit clair pour tout le monde.
Dans un usage basique, on se content de sélectionner un id, une classe ou un élément. Les sélecteurs JQuery permettent pourtant d’utiliser de puissantes et complexes expressions qui raviront les adeptes d’expressions régulières illisibles. Les outils de ces expressions se classent en deux familles : les selecteurs par hiérarchie et les sélecteurs par filtre. Le mélange savant de ces outils vous permettra de faire des sélections riches et précises.
La sélection par hiérarchie
Comme je l’ai dit plus haut, il faut penser le DOM sur le mode d’une arborescence. Il y a donc des nœuds enfants et des nœuds parents. JQuery permet de naviguer précisément dans cet arbre. Nous allons voir comment au travers de quelques exemples.
Note: j’ai d’abord pensé faire un fichier HTML d’exemple complexe auquel chaque exemple de sélecteur ferait référence, mais cela vous obligerait à remonter dans la page à chaque fois. Je vais donc dupliquer du code pour chaque exemple, au coût d’une page plus longue.
La sélection des descendants est la plus commune. Soit le code :
<div id="header"> <img src="logo" /> <ul> <li><a href="lien">lien1</a></li> <li><a href="lien2">lien2</a></li> </ul> </div> <div id="content"> <ul> <li><img src="photo1.jpg" /></li> <li><img src="photo2.jpg" /></li> </ul> </div>
(au passage, je signale qu’un img doit toujours avoir un attribut alt pour être valide en xhtml strict, et que chaque fois que vous utilisez un div inutile, vous tuez un chaton, mais j’utilise ici des raccourcis pour l’exemple).
Comment vais-je faire si je veux sélectionner les deux photos? Rien n’est plus simple :
$( '#content img' )
img placé après #content signifie qu’il faut selectionner tous les img descendant de #content. Ici, les img ne sont pas directement enfants de #content, mais puisqu’ils sont enfants de li eux-même enfants d’un ul lui-même enfant de #content, ils en sont descendants.
Si on ne veut pas sélectionner tous les descendants mais seulement les enfants directs, on utilise “>”. Ainsi :
<div id="content"> <img src="viagra.jpg" /> <ul> <li><img src="photo1.jpg" /></li> <li><img src="photo2.jpg" /></li> </ul> </div>
Si je veux sélectionner la publicité pour l’envoyer aux oubliettes, mais pas les photos (de préférences), je peux utiliser ceci :
$( '#content > img' )
Cela ne prenant en compte que les enfants directs, les photos, à la charge des li, sont ignorées sur le testament.
Vous devez maintenant saisir un peu mieux la nature des sélecteurs, nous allons pouvoir aller un peu plus vite. Les deux sélecteurs hiérarchiques suivants sont plus amusants, mais je vous dois une mise en garde : ils sont aussi plus sensibles aux modifications de votre code et peuvent causer des bugs difficiles à repérer.
Le sélecteur par proximité permet de sélectionner un élément directement adjacent à un autre.
$( 'li + img' )
Cela permet de sélectionner un img directement adjacent à un li (attention: le li lui-même n’est pas sélectionné).
Le sélecteur de fratrie permet de sélectionner tous les éléments de même type ayant un même parent, situé directement après un frère de référence :
<ul id="famille"> <li id="premier">enfant 1</li> <li id="second">enfant 2</li> <li id="troisieme">enfant 3</li> <li id="quatrieme">enfant 4</li> </ul><ul> </ul>
Ici, pour sélectionner tous les enfants qui ne sont pas l’ainé, on utilisera :
$( '#premier ~ li' )
Il faut aussi noter que certaines fonctions, ne relevant pas directement des sélecteurs, permettent d’aller encore plus loin, comme la fonction parent(), qui retourne le parent de l’élément sélectionné.
Vous savez donc maintenant traverser précisément l’arborescence du DOM. Des expressions complexes telles que $( ‘#content > ul p span + img’ ) sont tout à fait possible et vous permettent de manipuler des pages complexes. Mais ce n’est ici encore que de la sélection intermédiaire, JQuery nous offre encore d’avantage.
Les filtres
JQuery possède une très vaste collection de filtres permettant d’affiner vos sélections. Je ne vais pas tous les énumérer tant ils sont nombreux, mais je vais vous présenter ceux qui me semblent les plus utiles. Une fois que vous y serez familiarisés, et avec ce qui précède, la documentation officielle vous suffira à trouver votre chemin (oui, ce qu’on vient de voir ne représente pas un dixième de la page :] ).
:first est sans doute le filtre le plus utile lorsque vous voulez être certain de ne sélectionner qu’un seul élément. Il s’utilise ainsi : $( ‘#content ul:first’ ). Quelque soit le nombre de ul que contient #content, seul le premier sera sélectionné. Dans le même genre, il y a :last.
:eq(index) permet de sélectionner un élément comme si c’était l’élément d’un tableau (pour ceux qui ne sont pas familiarisés avec les tableaux en informatique, sachez qu’on compte à partir de 0). Je peux donc facilement sélectionner le troisième li du ul#items en faisant : $( ‘#items li:eq(2)’ ). De la même manière, il y a :gt(index) pour plus grand que (greater than) et :lt(index) pour plus petit que (lesser than).
:contains(text) permet de sélectionner un élément sur la base de son contenu. Ainsi, je peux sélectionner toutes mes notes en faisant : $( ‘p:contains(”N.B.”)’ ).
[attribut] permet de filtrer sur la base de l’existence d’un attribut. Cela prend tout son sens dans la forme [attribut=valeur]. Ainsi, je peux sélectionner toutes les images de ma maison en faisant : $( ‘img[src="images/maison.jpg"]‘ ). Une autre forme intéressante est [attribut*=valeur], le wildcard permettant de préciser une valeur qui ne soit pas exacte. Cela me permet par exemple de sélectionner tous les liens vers le site exemple.fr, quelque soit la page liée, ainsi : $( ‘a[href*="exemple.fr"]‘ ) .
Enfin, si vous codez du CSS, vous avez forcément déjà été ennuyé par l’impossibilité de faire référence à tous les titres en même temps, mais de devoir utiliser h1,h2,h3 ou encore ennuyé par le fait qu’un textfield et un submit sont tous les deux un élément input. JQuery facilite tout ça, grâce au filtre :heading et aux filtres de formulaire.
Pour sélectionner tous les titres, utilisez simplement $( ‘:heading’ ) . Pour sélectionner le bouton d’envoi d’un formulaire de commentaire, on utilise, par exemple : $( ‘#comment_form :submit’ ). Il existe un équivalent pour chaque type de input.
La cerise sur le gâteau : le contexte.
J’espère que vous avez digéré tout cela, c’était copieux. Après avoir joué un peu avec ces sélecteurs, vous devez entrevoir de vastes et complexes possibilités pour sélectionner précisément ce li caché au fin fond d’une série de div, et les div voisins ne vous ennuieront pas grâce à la puissante chaîne de filtres que vous avez organisée…
Oubliez tout.
JQuery, c’est la puissance, mais c’est aussi la simplicité. Pour éviter d’avoir à écrire des sélecteurs de trois lignes, un système de sélection en contexte a été développé. Imaginons le sélecteur suivant :
$( '#content ul:first > li:eq(3) > div.hilighted:gt(2) ul' )
Si je veux opérer sur plusieurs éléments dépendant de ces ul en plusieurs étapes, il serait fastidieux de réécrire ce sélecteur en le complexifiant encore. Le contexte permet de faire ceci :
var context = $( '#content ul:first > li:eq(3) > div.hilighted:gt(2) ul' ) ; $( 'li:eq(3)', context ).hide() ; $( 'li:first > a', context ).css( 'color', '#A37BCF' ) ;
Rien ne vous empêche ensuite de coder une fonction dont le contexte sera variable.
Voilà, j’espère que cette explication aura été éclairante, n’hésitez pas à me faire part de vos réactions.
Entraînez-vous à utiliser des selecteurs complexes puis, lorsque vous maîtriserez leurs différentes possibilités, vous pourrez passer à un autre niveau de compréhension. Paul Boag et Marcus Lillington ont avancé dans un podcast l’idée que lorsqu’une application correctement lancée s’approche de la maturité, elle connaît une période de stagnation et que pour sortir de cette stagnation, il faut simplifier. C’est ce que je vous propose dans un prochain article.
25 septembre, 2008 à 10:10
Je découvre ton blog via tes commentaires sur http://dany.canalmoins.fr et je dois dire que c’est une belle trouvaille, ce billet sur les selecteurs est vraiment tres interessant et tres instructif. Un bon rappel des bases et meme plus pour tous ceux qui utilisent cette fabuleuse lirairie qu’est jQuery.
25 septembre, 2008 à 13:56
Merci beaucoup, ravi que ça te plaise :)
24 février, 2010 à 7:14
Franchement, c’est vraiment intéressant et très bien expliqué ! Je garde la page en favoris, je vais y référer souvent ! ;) Tnx!
16 avril, 2010 à 12:40
Excellent !!