Filtrer une liste

Un petit bout de code html/css/javascript tout simple pour filtrer l'affichage des items d'une liste à partir d'une valeur tapée dans un champ. A étendre pour appliquer à des besoins plus sépcifiques.

<p><input type="text" id="myfilter"></p>
<ul id="mylist">
    <li>item 1</li>
    <li>item 2</li>
    <!-- … -->
    <li>item n</li>
</ul>
Code HTML d'u champ de filtrage et de la liste à filtrer.
/**
 *  Objet permettant de filtrer le contenu d'un liste en fonction du contenu
 *  d'un champ texte. Le filtrage s'appliquera aux enfants de la liste.
 */
class ListFilter {
    /**
     *  @param {HTMLElement|NodeList} data Liste à filtrer.
     *  @param {HTMLInputElement} field Champ de recherche
     *  @param {Boolean} [hideFiltering=false] Masquer ce qui est filtré
     */
    constructor (data, field, hideFiltering) {
        /**
         *  @readonly
         *  @type HTMLInputElement
         */
        this.field = field;

        /**
         *  @readonly
         *  @type HTMLElement
         */
        this.data = null;

        if (data instanceof HTMLUListElement) {
            this.data = data.querySelectorAll('li');
        } else if (data instanceof NodeList) {
            this.data = Array.from(data);
        } else {
            this.data = data;
        }

        if (!data || !this.data.forEach) {
            console.error('Wrong data'); // eslint-disable-line no-console
            return null;
        }
                
        this.filtering = hideFiltering || false;
        
        this.lastTyped = '';
        
        this.timer = null;
        this.timerListener = this.onDelay.bind(this);
        this.timerLoop = 0;
        
        this.current = {filtering: null, value: null};
        
        this.build();
    }
    
    build () {
        if (!this.field) {
            this.field = document.createElement('input');
            this.field.type = 'search';
            this.list.parentNode.insertBefore(this.field, this.list); 
        }
        this.field.addEventListener('keyup', this.onKeyUp.bind(this), false);
        this.show(this.field);
    }

    /**
     *  Ecouteur d'événement 'keyup' sur le champ
     *  @param {Event} e DOM Event
     */
    onKeyUp (e) {
        if (e.key === 'Escape') {
            this.reset();
            e.target.value = '';
        } else if (e.target.value !== this.lastTyped) {
            this.lastTyped = e.target.value;
            if (this.timer === null) {
                this.timer = window.setInterval(this.timerListener, 300);
            }
        }
    }
    
    onDelay () {
        if (this.lastTyped === this.field.value) {
            this.clearTimer();
            if (this.lastTyped) {
                this.process(this.lastTyped, this.filtering);
            } else {
                this.clear();
            }
        } else {
            this.timerLoop += 1;
            if (this.timerLoop > 10) {
                this.clearTimer();
            }
        }
    }
    
    clearTimer () {
        window.clearInterval(this.timer);
        this.timer = null;
        this.timerLoop = 0;
    }
    
    /**
     *  Effacer le filtre
     */
    clear () {
        this.data.forEach(this.show);
        this.current.value = null;
        this.current.filtering = null;
        this.lastTyped = '';
    }
    
    /**
     *  Effacer le filtre et la valeur tapée dans le champ de saisie.
     */
    reset () {
        this.clear();
        this.field.value = '';
    }
    
    /**
     *  @private
     *  @param {String} str Chaîne à rechercher.
     *  @param {Boolean} filtering Filtre (true) ou match (false).
     */
    process (str, filtering) {
        str = str.toLowerCase();
        if (str === this.current.value && filtering === this.current.filtering) {
            return;
        }
        this.current.value = str;
        this.current.filtering = filtering;
        this.data.forEach((item) => {
            if (item.textContent.toLowerCase().includes(str) === filtering) {
                this.hide(item);
            } else {
                this.show(item);
            }
        });
    }
    
    /**
     *  @private
     *  @param {HTMLElement} element Eléméent à masquer.
     */
    hide (element) {
        element.setAttribute('aria-hidden', 'true');
    }
    
    /**
     *  @private
     *  @param {HTMLElement} element Eléméent à afficher.
     */
    show (element) {
        element.setAttribute('aria-hidden', 'false');
    }
    
    /**
     *  Masquer les items qui contiennent une chaîne de caractères.
     *  @param {String} str Chaîne de caractères à chercher.
     */
    filter (str) { 
        this.process(str, true); 
    }
    
    /**
     *  Afficher uniquement les items qui contiennent une chaîne de caractères.
     *  @param {String} str Chaîne de caractères à chercher.
     */
    match (str) {
        this.process(str, false);
    }
}

export {ListFilter};
Code javascript pour gérer le filtrage de la liste.
#mylist {
    overflow: hidden;
    margin: 0 0 10px;
}
#mylist > li {
    float: left;
    list-style-position: inside;
    margin: 0 10px 0 0;
    -webkit-transition: margin linear 0.5s;
       -moz-transition: margin linear 0.5s;
         -o-transition: margin linear 0.5s;
            transition: margin linear 0.5s;
}
Code CSS minimal pour gérer le filtrage de la liste.


  • Afghanistan
  • Afrique du Sud
  • Albanie
  • Algérie
  • Allemagne
  • Andorre
  • Angola
  • Antigua et Barbuda
  • Arabie saoudite
  • Argentine
  • Arménie
  • Australie
  • Autriche
  • Azerbaïdjan
  • Bahamas
  • Bahreïn
  • Bangladesh
  • Barbade
  • Biélorussie
  • Belgique
  • Belize
  • Bénin
  • Bhoutan
  • Birmanie
  • Bolivie
  • Bosnie-Herzégovine
  • Botswana
  • Brésil
  • Brunei
  • Bulgarie
  • Burkina
  • Faso
  • Burundi
  • Cambodge
  • Cameroun
  • Canada
  • Cap-Vert
  • République centrafricaine
  • Chili
  • Chine
  • Chypre
  • Colombie
  • Comores
  • République du Congo
  • Rép. dém. du Congo
  • Corée du Nord
  • Corée du Sud
  • Costa Rica
  • Côte d’Ivoire
  • Croatie
  • Cuba
  • Danemark
  • Djibouti
  • République dominicaine
  • Dominique
  • Égypte
  • Émirats arabes unis
  • Équateur
  • Érythrée
  • Espagne
  • Estonie
  • États-Unis
  • Éthiopie
  • Fidji
  • Finlande
  • France
  • Gabon
  • Gambie
  • Géorgie
  • Ghana
  • Grèce
  • Grenade
  • Guatemala
  • Guinée
  • Guinée-Bissau
  • Guinée équatoriale
  • Guyana
  • Haïti
  • Honduras
  • Hongrie
  • Inde
  • Indonésie
  • Irak
  • Iran
  • Irlande
  • Islande
  • Israël
  • Italie
  • Jamaïque
  • Japon
  • Jordanie
  • Kazakhstan
  • Kenya
  • Kirghizistan
  • Kiribati
  • Koweït
  • Laos
  • Lesotho
  • Lettonie
  • Liban
  • Liberia
  • Libye
  • Liechtenstein
  • Lituanie
  • Luxembourg
  • Macédoine
  • Madagascar
  • Malaisie
  • Malawi
  • Maldives
  • Mali
  • Malte
  • Maroc
  • Îles Marshall
  • Maurice
  • Mauritanie
  • Mexique
  • Micronésie
  • Moldavie
  • Monaco
  • Mongolie
  • Monténégro
  • Mozambique
  • Namibie
  • Nauru
  • Népal
  • Nicaragua
  • Niger
  • Nigeria
  • Norvège
  • Nouvelle-Zélande
  • Oman
  • Ouganda
  • Ouzbékistan
  • Pakistan
  • Palaos
  • Panamá
  • Papouasie Nouvelle-Guinée
  • Paraguay
  • Pays-Bas
  • Pérou
  • Philippines
  • Pologne
  • Portugal
  • Qatar
  • Russie
  • Salvador
  • Syrie
  • République tchèque
  • Tanzanie
  • Roumanie
  • Royaume-Uni
  • Rwanda
  • Sainte-Lucie
  • Saint-Christophe et Niévès
  • Saint-Marin
  • Saint-Vincent et les Grenadines
  • Salomon
  • Samoa
  • Sao Tomé et Principe
  • Sénégal
  • Serbie
  • Seychelles
  • Sierra Leone
  • Singapour
  • Slovaquie
  • Slovénie
  • Somalie
  • Soudan
  • Soudan du Sud
  • Sri Lanka
  • Suède
  • Suisse
  • Suriname
  • Swaziland
  • Tadjikistan
  • Tchad
  • Thaïlande
  • Timor oriental
  • Togo
  • Tonga
  • Trinité et Tobago
  • Tunisie
  • Turkménistan
  • Turquie
  • Tuvalu
  • Ukraine
  • Uruguay
  • Vanuatu
  • Venezuela
  • Viêt Nam
  • Yémen
  • Zambie
  • Zimbabwe
Exemple d'utilisation.