Espace tutos dev’ & informatique 🐧

Coder sans jQuery : Filtrage d’éléments

Publié le
Article sur comment coder sans jquery filtrage elements

Introduction

C’est l’heure d’un nouvel article sur le remplacement du code jQuery legacy ! Pas exactement l’article que j’avais prévu (celui sur les écouteurs d’évènements), car je me suis rendu compte qu’il manquait encore des fonctions de base pour la mise en place de ces derniers (notamment pour les évènements par délégation).

Nous allons donc combler ce manque, et approfondir un peu le remplacement des fonctions de filtrage des éléments fournies par jQuery. C’est dans la suite directe de l’article sur le remplacement de $(‘selector’), il est préférable de l’avoir déjà lu avant de consulter celui-ci.

Note : comme la plupart des codes sources proposés sur le blog, le code présent dans cet article est compatible avec Internet Explorer à partir de la version 11.

Matching d’élément : remplacement de $.is(‘selector’)

Ce que fait $.is(‘selector’)

La fonction is('selector') des objets jQuery valide si le ou les éléments contenus dans cet objet satisfont le sélecteur donné. La syntaxe des sélecteurs est la même que pour $('selector'), c’est à dire les sélecteurs CSS3 et les sélecteurs spécifiques jQuery :

var elements = $("[…]"); // on sélectionne des éléments

var isParagraphe = elements.is("p"); // true si tous les éléments sont des paragraphes
var isClasse = elements.is(".classe"); // true si tous les éléments ont la classe donnée
var isVisible = elements.is(":visible"); // true si tous les éléments sont visibles

Remplacement de $.is(‘selector’) : la fonction matches(‘selector’)

Le remplacement direct pour cette fonction en javascript natif est la fonction matches('selector') présente sur les objets de type Element tels que retournés par querySelector('selector'). Elle supporte les sélecteurs reconnus par le navigateur du visiteur, tous les sélecteurs CSS3 devraient fonctionner. Le seul souci, c’est que pendant un certain temps chaque navigateur a eu sa propre implémentation avec son propre nom, du coup il est préférable de la polyfiller avant de s’en servir. La fondation Mozilla offre ce code :

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.matchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector ||
    Element.prototype.oMatchesSelector ||
    Element.prototype.webkitMatchesSelector ||
    function(s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i = matches.length;
        while (--i >= 0 && matches.item(i) !== this) {}
        return i > -1;
    };
}

On peut ensuite s’en servir tout aussi facilement que $.is('selector') :

var element = document.querySelector("[…]"); // on sélectionne un élément

var isParagraphe = element.matches("p"); // true si l'élément est un paragraphe
var isClasse = element.matches(".classe"); // true si l'élément a la classe donnée

Pour ce qui est des NodeList (telles que renvoyées par querySelectorAll('selector')), ces dernières ne proposent pas nativement de fonction matches('selector'), mais rien ne nous empêche d’en ajouter une avec du code maison :

NodeList.prototype.matches = function (selector) {
  for(var i = 0; i < this.length; i++){
    if( ! (this[i].matches && this[i].matches(selector)) ) return false;
  }
  return true;
};

L’utilisation est similaire à précédemment :

var elements = document.querySelectorAll("[…]"); // on sélectionne des éléments

var isParagraphe = elements.matches("p"); // true si tous les éléments sont des paragraphes
var isClasse = elements.matches(".classe"); // true si tous les éléments ont la classe donnée

Evidemment, cela ne fonctionne pas avec les sélecteurs spécifiques jQuery, pour ceux là il faudra revoir la logique et se contenter des sélecteurs CSS3 standards.

Filtrage d’éléments : remplacement de $.filter(‘selector’)

Ce que fait $.filter(‘selector’)

A partir d’un objet jQuery contenant plusieurs éléments, $.filter('selector') extrait les éléments qui satisfont le sélecteur passé en paramètre. Comme d’habitude, les sélecteurs sont CSS3 ou spécifiques jQuery :

var elements = $("[…]"); // on sélectionne des éléments

var paragraphes = elements.filter("p"); // on prend les paragraphes
var avecClasse = elements.filter(".classe"); // on prend  les éléments avec la classe donnée
var visibles = elements.filter(":visible"); // on prend les éléments visibles

A noter que $.filter('selector') peut également prendre en paramètre une fonction à la place du sélecteur, qui sera appelée pour chaque élément, et renverra true si l’élément satisfait le filtre.

Remplacement de $.filter(‘selector’) : fonction filter maison

Les NodeList retournés par querySelectorAll('selector') n’incluent pas de fonction de filtrage, il va donc falloir en ajouter une. En s’inspirant de la signature de Array.filter(function), nous allons supporter 2 types d’appels : une forme NodeList.filter('selector') et une forme NodeList.filter(function). Rien de bien méchant, il s’agit juste de boucler sur la NodeList initiale, de tester chaque élément soit avec le sélecteur (la fonction matches('selector'), vous vous rappelez ?) soit avec la fonction, et de ne renvoyer que les éléments satisfaisant le filtre. On ne pourra pas renvoyer notre propre NodeList (il n’y a pas de constructeur ou de fonction d’ajout d’élément à une NodeList), on se contentera donc d’un simple tableau d’éléments.

NodeList.prototype.filter = function (callbackOrSelector) {
  var callback = typeof callbackOrSelector == "function" ? callbackOrSelector : function(el){ return el.matches && el.matches(callbackOrSelector) };
  var filtered = [];
  for(var i = 0; i < this.length; i++){
    var element = this[i];
    if(callback.call(element, element, i)) filtered.push(element);
  }
  return filtered;
};

L’utilisation est aussi simple que $.filter(‘selector’) :

var elements = document.querySelectorAll("[…]"); // on sélectionne des éléments

var paragraphes = elements.filter("p"); // on prend les paragraphes
var avecClasse = elements.filter(".classe"); // on prend les éléments avec la classe donnée

var avecFonction = elements.filter(function(el, index){
  return el.classList.contains("classe");
}); // on prend les éléments qui renvoient une valeur assimilable à true

Comme d’habitude, les sélecteurs spécifiques jQuery ne sont pas de la partie, il va falloir changer de logique. Le tableau renvoyé par la fonction maison ne supporte pas de filtrage par sélecteur (ce n’est pas une NodeList), il faudra s’en contenter, mais c’est suffisant pour beaucoup de cas d’utilisation.

A noter qu’il est possible de boucler sur le tableau pour traiter les éléments les uns après les autres avec la fonction forEach() – bien supportée par les navigateurs y compris Internet Explorer 11 – comme avec notre implémentation personnalisée de forEach() pour les NodeList.

Conclusion

Voila, c’en est fini des appels à $.is('selector') et $.filter('selector'), longue vie à matches('selector') et au petit nouveau filter('selector') !
Bonne optimisation.

Références

Mozilla Developers – Element.matches()
jQuery Documentation – $.is()
jQuery Documentation – $.filter()

Photo de profil de Loïc Burtin Code Colibri développeur web à Dijon

A propos de l'auteur

Loïc Burtin

Développeur web indépendant, avec une tendance à couper les plumes en quatre pour que les sites se chargent plus rapidement.

Partagez cet article  =>