Intéressé par des cours d'informatique en ligne ?
Visitez mon nouveau site https://www.yesik.it !

Le propre de la programmation impérative est que c'est le programmeur qui donne l'ordre et la liste des instructions à exécuter dans un programme. Or il arrive parfois que l'on désire que certaines de ces instructions de soient exécutées que dans certains cas. On parle d'exécution conditionnelle.

Pour répondre à ce besoin, les langages de programmation proposent des structures de contrôles spécifiques. Nous allons voir ici la plus commune, la structure de contrôle conditionnelle si ... alors ... sinon ... (if ... else).

Un exemple très simple

Pour commencer, nous allons voir un exemple très simple. Celui-ci affiche un message de bienvenue personnalisé si le nom de l'utilisateur est précisé sur la ligne de commande, et un message standard sinon.

Pseudo-code

En pseudo-code, le fonctionnement de ce programme est des plus simples à exprimer:

le programme principal fait:
    si un nom est reçu en argument alors:
        afficher "Heureux de vous revoir ${utilisateur}"
    sinon:
        afficher "Bienvenue"

Java

Dans sa version de base, le programme Java ne recèle aucune complexité:

public class Bienvenue {
    static public void main(String[] args) {
        if (args.length > 0) {
            System.out.println("Heureux de vous revoir " + args[0]);
        }
        else {
            System.out.println("Bienvenue");
        }
    }
}

La syntaxe est assez facile à comprendre:

Vous pouvez compiler et exécuter ce programme ainsi:

sh$ javac Bienvenue
sh$ java Bienvenue
Bienvenue
sh$ java Bienvenue Sylvain
Heureux de vous revoir Sylvain

Voilà un petit programme simple, rapide et qui fonctionne! Enfin, que l'on peut tout de même améliorer. En effet, que se passe-t-il si l'on donne plusieurs noms sur la ligne de commande?

sh$ java Bienvenue John Paul Georges Ringo
Heureux de vous revoir John

Effectivement, le programme ne salue que le premier. C'est un comportement qui peut être acceptable. Néanmoins, il est quand même d'usage de signaler à l'utilisateur qu'il a invoqué notre programme avec un nombre d'arguments invalides. Bref, nous n'avons plus à gérer deux cas (un nom ou pas de nom), mais trois (un nom, pas de nom, ou plus d'un nom). Le programme devient alors:

égal à ==
différent de !=
inférieur à <
inférieur ou égal à <=
supérieur à >
supérieur ou égal à >=
public class Bienvenue {
    static public void main(String[] args) {
        if (args.length == 1) {
            System.out.println("Heureux de vous revoir " + args[0]);
        }
        else if (args.length == 0) {
            System.out.println("Bienvenue");
        }
        else {
            System.err.println("Trop d'arguments!");
        }
    }
}

Comme vous le devinez, l'opérateur == sert à tester l'égalité entre deux valeurs. Il existe bien d'autres opérateurs de comparaison que vous trouverez dans le tableau ci-dessus.

Remarque:

Vous avez peut-être remarqué que dans le cas où il y a trop d'arguments, nous affichons le message sur la sortie d'erreur. Pas sur la sortie standard:

           System.err.println("Trop d'arguments!");

C'est l'usage pour signaler les anomalies de fonctionnement. En général, la sortie d'erreur et la sortie standard sont toutes deux dirigées vers la console (l'écran). Néanmoins, l'utilisateur peut avoir redirigé la sortie standard, par exemple vers un fichier. Dans ce cas, utiliser la sortie d'erreur pour signaler les situations invalides garantit que l'utilisateur verra tout de même le message.

C++

Tout ce que nous venons de voir se transpose facilement en C++. Le programme étant quasiment le même:

#include <iostream>
 
int main(int argc, char * argv[])
{
    if (argc == 2) {
        std::cout << "Heureux de vous revoir " << argv[1] << std::endl;
    }
    else if (argc == 1) {
        std::cout << "Bienvenue" << std::endl;
    }
    else {
        std::cerr << "Trop d'arguments!" << std::endl;
    }
}

Un exemple un peu plus sérieux

Pour ce second exemple, nous allons réaliser un programme capable d'afficher la liste des produits dont le nom correspond – au moins en partie – à celui donné par l'utilisateur sur la ligne de commande.

L'ensemble des produits sera mémorisé dans un tableau, et une boucle servira à parcourir ce tableau. Enfin, un test permettra de savoir si le nom du produit correspond à ce que voulait l'utilisateur.

Pseudo-code

En pseudo-code, le programme peut se décrire:

Le programme principal fait:
    définir produits ← {
        "Shampooing gel antipelliculaire",
        "Shampooing éclat protecteur",
        "Lait douceur corps",
        "Douche douceur",
        "Gel douche",
        "Shampooing stimulant"
    }

    pour chaque produit dans produits faire:
        si le produit correspond à celui recherché alors:
            afficher produit

si sans sinon

Vous le constatez, la clause sinon est optionnelle.

Ici, il n'y a rien de spécial à faire si la condition n'est pas satisfaite, donc pas de sinon...

Java

La transcription en Java du programme n'est pas très difficile. Nous pouvons d'ores et déjà écrire une bonne partie du programme:

public class Cosmetiques {
    public static void main(String args[]) {
        String[]    produits = {
            "Shampooing gel antipelliculaire",
            "Shampooing éclat protecteur",
            "Lait douceur corps",
            "Douche douceur",
            "Gel douche",
            "Shampooing stimulant"
        };
 
        for(String produit : produits) {
            /*
             *  si le produit correspond à celui recherché alors:
             *     afficher produit
             */
        }
    }
}

Il reste à résoudre le problème de savoir comment déterminer si le nom d'un produit correspond à celui demandé par l'utilisateur. En fait, si l'utilisateur cherche un produit "protecteur", il nous faut une méthode capable de dire:

Bref, il nous faut pouvoir déterminer si une chaîne de caractères en contient ou pas une autre. En Java, la méthode contains fait l'affaire:

public class Cosmetiques {
    public static void main(String args[]) {
        String[]    produits = {
            "Shampooing gel antipelliculaire",
            "Shampooing éclat protecteur",
            "Lait douceur corps",
            "Douche douceur",
            "Gel douche",
            "Shampooing stimulant"
        };
 
        for(String produit : produits) {
            if (produit.contains(args[0])) {
                System.out.println(produit);
            }
        }
    }
}

Comme toujours, compilons et testons:

sh$ javac Cosmetiques
sh$ java Cosmetiques Shampooing
Shampooing gel antipelliculaire
Shampooing éclat protecteur
Shampooing stimulant

La recherche fonctionne également avec des portions de mot – puisque la méthode contains se contente de vérifier que la chaîne recherchée est contenue dans l'autre:

sh$ java Cosmetiques dou
Lait douceur corps
Douche douceur
Gel douche

Par contre, problème:

sh$ java Cosmetiques
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0

Forcément, s'il n'y a pas d'argument, quand on essaye d'y accéder ligne 13 du programme, la machine virtuelle Java détecte quelque chose d'anormal. Il va donc falloir gérer ce cas.

On pourrait se contenter d'un message d'erreur si le nombre d'arguments est invalide. Mais, dans le cadre de notre programme, il est peut-être plus intelligent d'afficher l'ensemble de tous les produits si aucun critère n'est retenu. Ainsi, le programme agirait un peu comme un filtre.

La première idée pour atteindre ce but serait de ré-écrire la boucle ainsi:

/* ... */
        for(String produit : produits) {
            if (args.length == 0) {
                System.out.println(produit);
            }
            else if (produit.contains(args[0])) {
                System.out.println(produit);
            }
        }
/* ... */

Le premier test vérifie s'il y a un critère de recherche. S'il n'y en a pas, on affiche le nom du produit. Dans le cas contraire, le second test conditionne l'affichage au fait que le nom du produit concorde avec ce qui est recherché.

sh$ java Cosmetiques dou
Lait douceur corps
Douche douceur
Gel douche
sh$ java Cosmetiques lavande
sh$ java Cosmetiques
Shampooing gel antipelliculaire
Shampooing éclat protecteur
Lait douceur corps
Douche douceur
Gel douche
Shampooing stimulant

Ca fonctionne, mais ça n'est pas forcément très élégant. Ce qui serait bien, ce serait de dire:

si il n'y a aucun critère de recherche ou le critère correspond alors:
    ...

En Java, comme dans tous les langages dont la syntaxe est inspirée du C (C++, Java, Groovy, JavaScript, PHP, etc.), il est possible de combiner les tests avec des opérateurs et logique et ou logique':

ou logique ||
et logique &&
public class Cosmetiques {
    public static void main(String args[]) {
        String[]    produits = {
            "Shampooing gel antipelliculaire",
            "Shampooing éclat protecteur",
            "Lait douceur corps",
            "Douche douceur",
            "Gel douche",
            "Shampooing stimulant"
        };
 
        for(String produit : produits) {
            if ( (args.length == 0) || (produit.contains(args[0])) ) {
                System.out.println(produit);
            }
        }
    }
}

Opérateur paresseux

Le ou logique et le et logique sont des opérateurs paresseux: ils n'évaluent leurs arguments que si c'est nécessaire. Ainsi:

  • dans un ou logique, si le terme de gauche est vrai, le terme de droite n'est pas évalué (si le terme à gauche est vrai le ou logique sera vrai quelque soit le terme de droite!)
  • dans un et logique, si le terme de gauche est faux, le terme de droite n'est pas évalué (si le terme à gauche est faux le et logique sera faux quelque soit le terme de droite!)

Toute dernière modification, que faire si l'utilisateur passe trop d'arguments sur la ligne de commande? A nouveau, un simple test permet de donner un message significatif à l'utilisateur:

public class Cosmetiques {
    public static void main(String args[]) {
        String[]    produits = {
            "Shampooing gel antipelliculaire",
            "Shampooing éclat protecteur",
            "Lait douceur corps",
            "Douche douceur",
            "Gel douche",
            "Shampooing stimulant"
        };
 
        if (args.length > 1) {
            System.err.println("Usage: java Cosmetiques [critere]");
        }
        else for(String produit : produits) {
            if ( (args.length == 0) || (produit.contains(args[0])) ) {
                System.out.println(produit);
            }
        }
    }
}

Groovy

La version Groovy du programme est sensiblement la même:

public class Cosmetiques {
    static main(def args) {
        def    produits = [
            "Shampooing gel antipelliculaire",
            "Shampooing éclat protecteur",
            "Lait douceur corps",
            "Douche douceur",
            "Gel douche",
            "Shampooing stimulant"
        ];
 
        if (args.size() > 1) {
            System.err.println("Usage: groovy Cosmetiques [critère]");
        }
        else for(produit in produits) {
            if ( (!args.size()) || (produit.contains(args[0])) ) {
                println produit;
            }
        }
    }
}

Mis à part les différences de syntaxe, le seul point notable concerne le test:

           if ( (!args.size()) || (produit.contains(args[0])) ) {
               println produit;
           }

Ici, le point d'exclamation (!) est le non logique. Cette notation est synonyme de égal à zéro. Ainsi, le même test exactement pourrait avoir été écrit ainsi:

           if ( (args.size() == 0) || (produit.contains(args[0])) ) {
               println produit;
           }

C++

La version C++ du programme est très similaire à la version Java:

#include <string>
#include <iostream>
 
int main(int argc, const char* argv[])
{
    std::string   produits[] = {
        "Shampooing gel antipelliculaire",
        "Shampooing éclat protecteur",
        "Lait douceur corps",
        "Douche douceur",
        "Gel douche",
        "Shampooing stimulant"
    };
 
    if (argc > 2) {
        std::cout << "Usage: " << argv[0] << " [critère]" << std::endl;
    }
    else for(unsigned int i = 0; i < sizeof(produits)/sizeof(std::string); i=i+1)
    {
        if ( (argc == 1) || (produits[i].find(argv[1]) != std::string::npos) )
        {
            std::cout << produits[i] << std::endl;
        }
    }
 
    return 0;
}

Comme souvent, la syntaxe est autrement plus complexe qu'en Java ou Groovy. En plus de cela, le test qui détermine la présence ou l'absence de la sous-chaîne recherchée dans le nom d'un produit est aussi plus complexe:

       if ( (argc == 1) || (produits[i].find(argv[1]) != std::string::npos) )
       {
           std::cout << produits[i] << std::endl;
       }

En effet, la documentation de C++ nous indique que la méthode find renvoie:

Par conséquent, si la méthode find renvoit n'importe quoi d'autre que pas trouvé (std::string::npos), cela veut bien dire que le nom du produit contient bien la chaîne recherchée.

PHP

Le programme PHP équivalent va beaucoup ressembler aux différents programmes que nous avons vu jusqu'à présent. A ceci près que PHP est un langage qui va être interprété par un serveur web – et qui va renvoyer une page web au client.

Par conséquent, PHP ne peut pas accéder aux arguments passés sur la ligne de commande, tout simplement parce que le programme n'est pas invoqué à partir du shell. Le plus proche équivalent sera de récupérer les arguments de la requête http.

Avant de rentrer dans ces détails, voici une première version du code:

<?php
    $produits = array(
       "Shampooing gel antipelliculaire",
        "Shampooing éclat protecteur",
        "Lait douceur corps",
        "Douche douceur",
        "Gel douche",
        "Shampooing stimulant"
    );
 
    print("<ul>");
    $critere = array_key_exists("critere", $_GET) ? $_GET["critere"] : "";
    foreach($produits as $produit)
    {
        /* !!! Le test ci-dessous est incorrect !!! */
        if ( ($critere == "") || (strpos($produit, $critere) != false) )
        {
            print("<li>$produit</li>");
        }
    }
    print("</ul>");

Vous le voyez au commentaire dans le code, quelque-chose ne va pas dans ce programme. Avant d'y revenir, nous allons quand même tester le programme et voir que pourtant il a l'air de fonctionner...

Donc si vous recopiez le code précédent dans un fichier cosmetiques.php du répertoire de votre serveur web, vous pouvez y accéder en vous connectant à l'URL http://localhost/cosmetiques.php. Vous pouvez le faire avec un navigateur comme Firefox, Iceweasel ou Internet Explorer:

On peut aussi passer des arguments dans la requête http. Ces arguments se passent en fin d'URL – séparés du reste de l'adresse par une point d'interrogation comme http://localhost/cosmetiques.php?critere=dou

Et maintenant, la surprise! Celle qui montre que le programme n'est pas correct! Essayons de charger les articles qui contiennent le mot "Douche". On s'attend légitimement à en trouver un. Or l'URL http://localhost/cosmetiques.php?critere=Douche ne donne aucun résultat...
Que s'est-il passé? Et bien le problème vient du fait que contrairement aux autres langages que nous avons utilisé dans cet article PHP est un langage à typage faible. En clair, cela veut dire qu'au besoin, l'interpréteur PHP peut convertir les données d'un type à l'autre. Cela a une importance considérable dans toutes les expressions conditionnelles:

       if ( ($critere == "") || (strpos($produit, $critere) != false) )
       ...

D'après la documentation de PHP, strpos renvoit:

Et en apparence, notre test compare bien le résultat avec la valeur false, et utilise le résultat de cette comparaison pour déterminer si le produit doit être affiché ou pas.

Voyons donc ce qui se passe lorsqu'on cherche les produits qui contiennent le mot "Douche". Le programme trouve bien un produit. Mais strpos dans ce cas renvoie la position du mot "Douche" dans le nom du produit trouvé. En l'occurrence, comme le nom du produit commence par le mot "Douche" la position est 0 (zéro). Le test devient donc:

      if ( ... (0 != false) )
      ...

Or par la magie du typage faible, l'interpréteur détectant la comparaison de deux types différents (l'entier 0 et le booléen false), il prend la liberté de convertir en un type commun. Or, d'après les règles de PHP, false convertit en entier devient 0. Bref, le test est:

      if ( ... (0 != 0) )
      ...

Ce qui est évidemment faux! Donc le produit n'est pas sélectionné pour l'affichage.

C'est une spécificité de PHP. Pour résoudre ce problème, il est conseillé de systématiquement utiliser les opérateurs de comparaison stricts (c'est à dire ne faisant pas de conversion). Ces opérateurs sont au nombre de deux:

Si l'on ré-écrit le programme avec ces opérateurs, vous constaterez que maintenant le programme fonctionne.

       if ( ($critere === "") || (strpos($produit, $critere) !== false) )
       {
           print("<li>$produit</li>");
       }

Juste pour terminer, nous allons ajouter un peu de HTML autour de notre script PHP, histoire d'avoir un formulaire facile d'emploi. Au final, voici le code complet du programme:

<html>
<head><title>Cosmetiques</title></head>
<body>
<form action="#">
    <input type="text" name="critere" />
    <input type="submit" />
</form>
<?php
    $produits = array(
       "Shampooing gel antipelliculaire",
        "Shampooing éclat protecteur",
        "Lait douceur corps",
        "Douche douceur",
        "Gel douche",
        "Shampooing stimulant"
    );
 
    print("<ul>");
    $critere = array_key_exists("critere", $_GET) ? $_GET["critere"] : "";
    foreach($produits as $produit)
    {
        if ( ($critere === "") || (strpos($produit, $critere) !== false) )
        {
            print("<li>$produit</li>");
        }
    }
    print("</ul>");
?>
</body>
</html>

JavaScript

Approche basique

Nous pouvons reprendre avec JavaScript une approche similaire à celle de la version PHP du programme: générer dynamiquement la liste des produits qui correspondent à un critère.

Le document HTML maître est classique:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 traditional//EN" "http://www.w3.org/TR/xhtml1/dtd/xhtml1-traditional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xml:lang="fr"
    lang="fr"
    dir="ltr">
<head>
    <title>Cosmétiques</title>
</head>
<body>
<script type="text/javascript" src="cosmetiques.js">
</script>
</body>
</html>

Quand au programme JavaScript à proprement parler, sa structure est elle aussi des plus communes:

var Cosmetiques = function() {
    var     that = {};
 
    /* TODO: initialiser les données membres */
    /* TODO: définir les méthodes */
 
    return that;
};
 
var cosmetiques = Cosmetiques();
cosmetiques.affiche();

Initialiser le tableau des produits ne recèle aucun problème non plus:

var Cosmetiques = function() {
    var     that = {};
 
    var     produits = [
        "Shampooing gel antipelliculaire",
        "Shampooing éclat protecteur",
        "Lait douceur corps",
        "Douche douceur",
        "Gel douche",
        "Shampooing stimulant"
    ];
 
/* ... */

Quand à la méthode affiche, celle-ci va reposer sur l'utilisation de la propriété location.search. celle-ci contient la partie de l'URL située après le ? point interrogation. Ainsi, pour l'URL http://localhost/cosmetiques.html?douche, cette propriété vaudra ?douche. Remarquez que le point d'interrogation est inclus!

Par conséquent pour extraire juste le critère de filtrage sans ce point d'interrogation, un peu de manipulation de chaîne sera nécessaire avec la String.substring. Enfin, la concordance se fait de manière similaire à tout ce que nous avons vu précédemment, avec la méthode String.indexOf.

Au final, le code de la méthode affiche est le suivant:

/* ... */
    that.affiche = function() {
        var filtre = location.search;
 
        var i = 0;
        document.writeln("<ul>");
        while(i < produits.length) {
            if ((filtre.length < 2) || (produits[i].indexOf(filtre.substring(1)) != -1)) {
                document.writeln("<li>" + produits[i] + "</li>");
 
            }
            i=i+1;
        }
        document.writeln("</ul>");
    }
/* ... */

Muni de ce code, vous pouvez tester en chargeant par exemple les URL:

file:///chemin/vers/cometiques.html
file:///chemin/vers/cometiques.html?
file:///chemin/vers/cometiques.html?douche

Approche accessible

Le programme précédent fonctionne. Mais que se passerait-il si l'utilisateur a désactivé JavaScript? Ou si son navigateur ne le supporte pas? Essayez: cherchez dans les préférences de votre navigateur comment désactiver JavaScript, et vous constaterez qu'en rechargeant la page, celle-ci reste désespérément vide...

Évidemment un site Web peut difficilement se contenter d'une philisophie qui se résume à "Pas de JavaScript? Pas de site!"...

Nous allons donc voir comment mettre en oeuvre JavaScript de manière non-obstructive. C'est à dire que le site fonctionnera sans JavaScript, mais simplement mieux si JavaScript est activé.

La première modification va être d'inclure dans le document HTML la liste des produits. Ainsi, même les navigateurs sans support pour JavaScript afficheront la liste des produits.

<html xmlns="http://www.w3.org/1999/xhtml"
    xml:lang="fr"
    lang="fr"
    dir="ltr">
<head>
    <title>Cosmétiques</title>
</head>
<body>
 
<ul id="produits">
    <li>Shampooing gel antipelliculaire</li>
    <li>Shampooing éclat protecteur</li>
    <li>Lait douceur corps</li>
    <li>Douche douceur</li>
    <li>Gel douche</li>
    <li>Shampooing stimulant</li>
</ul>
 
<script type="text/javascript" src="cosmetiques.js">
</script>
</body>
</html>


Remarque:

Si cela vous dérange de coder en dur la liste des produits dans le document HTML, n'oubliez pas que JavaScript est interprété côté client.

Par contre, du côté de votre serveur, il est toujours possible de générer dynamiquement le fichier HTML précédent. Par exemple à l'aide d'un script PHP...

Maintenant, concentrons nous sur les navigateurs avec support pour JavaScript. Pour ces navigateurs, nous allons ajouter une fonctionnalité pour filtrer les résultats.

La structure du fichier .js reste identique à la version précédente:

var Cosmetiques = function() {
    var     that = {};
 
    that.affiche = function() {
        /* TOTO: Cacher les éléments qui ne correspondent pas au filtre */
    }
 
    return that;
};
 
var cosmetiques = new Cosmetiques();
cosmetiques.affiche();

En fait, la subtilité ici est révélée par le commentaire dans le code ci-dessus. En effet, la version précédente du programme commençait avec une liste de produit vide dans le document HTML, et affichait ceux qui correspondaient au filtre. Maintenant, nous allons faire exactement le contraire: partir avec tous les produits déjà dans le document HTML, et cacher ceux qui ne correspondent pas au filtre.

Tout notre problème consiste donc à pouvoir accéder à des éléments déjà présents dans le document. Cela est rendu possible grâce à une technologie appelée DOM (pour Document Object Model) et incluse dans les navigateurs modernes.

DOM permet d'accéder et modifier les éléments du document. Entre autres fonctionnalités, DOM permet de retrouver un élément par son type (avec la méthode document.getElementsByTagName) ou par son id (avec la méthode document.getElementById).

La méthode affiche va donc commencer par retrouver "les éléments li qui se trouvent dans l'élément id='produits'":

/* ... */
    that.affiche = function() {
        var liste       = document.getElementById("produits");
        var items       = liste.getElementsByTagName("li");

Récupérer le critère de tri ne change pas:

/* ... */
        var filtre      = location.search;

Enfin une boucle parcourt chacun des élément trouvés:

for(i = 0; i < items.length; i = i+1) {
           /* TODO: cacher items[i] s'il ne correspond pas au filtre */
        }

Reste à accéder au texte de l'élément. Et le cas échéant à cacher ce dernier. Curieusement, accéder au texte de l'élément est un peu alambiqué. En effet, dans le modèle DOM, le texte est vu comme le contenu d'un élément. Ou, dans la terminologie de DOM, le texte est un noeud fils de l'élément. Enfin, une fois ce noeud obtenu, pour récupérer le texte effectif qu'il contient, il faut encore accéder à la propriété nodeValue. Ce qui se résume en une ligne de JavaScript:

var label = items[i].firstChild.nodeValue;

Piège:

Ici, je suppose implicitement que les éléments li de ma liste de produit ne contiennent que du texte. Et pas d'autres éléments.

Ainsi, le code ci-dessus ne peut pas gérer les éléments suivants:

<ul id="produits">
    <li><em>Shampooing</em> gel antipelliculaire</li>

Enfin, pour cacher ou afficher un élément, il reste à changer sa propriété de style display:

/* ... */
            if ((filtre.length < 2) || (label.indexOf(filtre.substring(1)) != -1))
                items[i].style.display = "list-item";
            else
                items[i].style.display = "none";

Note:

La propriété style d'un élément permet d'accéder aux ... propriétés de style de l'élément. Tout comme on pourrait le faire en CSS.

Vous pouvez bien sûr changer d'autres propriétés que display. Ainsi, si vous voulez changer par programme la couleur du texte, vous pouvez utiliser une instruction de la forme:

items[i].style.color = "red";

Voir: https://developer.mozilla.org/en/DOM/element.style

En remettant bout à bout les fragments de code détaillés ci-dessus, vous devriez arriver à la méthode affiche suivante:

/* ... */
    that.affiche = function() {
        var liste       = document.getElementById("produits");
        var items       = liste.getElementsByTagName("li");
 
        var filtre      = location.search;
 
        for(i = 0; i < items.length; i = i+1) {
            var label = items[i].firstChild.nodeValue;
            if ((filtre.length < 2) || (label.indexOf(filtre.substring(1)) != -1))
                items[i].style.display = "list-item";
            else
                items[i].style.display = "none";
        }
    }
/* ... */

A nouveau, vous pouvez tester en chargeant quelques URL:

file:///chemin/vers/cometiques.html
file:///chemin/vers/cometiques.html?
file:///chemin/vers/cometiques.html?Sham

Pour quelques requêtes de moins

Il reste tout de même un défaut dans le programme précédent. En effet, mettre à jour la page (par exemple avec un nouveau filtre) nécessite de la recharger. Dans le contexte d'une application web, cela signifie un cycle requête-réponse complet.

Ca serait compréhensible avec une solution bâtie autour de PHP qui est interprété côté serveur. Mais là, il s'agit de JavaScript! Un langage interprété côté client.

Or, puisque le client dispose de la liste complète des produits, à quoi sert-il de ré-interroger le serveur à chaque fois?

En fait, la seule raison pour laquelle nous observons ce comportement, c'est que nous utilisons la partie recherche de l'URL pour déterminer le critère de sélection de produits à afficher. Le seul moyen de mettre à jour cette valeur, est de charger une nouvelle URL dans le navigateur. D'où re-chargement du document. Pas très efficace, ça...

Une solution possible serait d'ajouter un formulaire à la page. Comme le formulaire n'a d'intérêt que si le client supporte JavaScript, le formulaire va être généré par notre script. Ajoutez donc les lignes suivantes au début de votre fichier cosmetiques.js:

document.writeln("<form action='#'>");
document.writeln("<input type='text' id='filtre' />");
document.writeln("<input type='button' value='Chercher' id='affiche' />");
document.writeln("</form>");

Maintenant, il nous faut faire deux modifications:

  1. La première pour associer le bouton "Chercher" avec l'appel à cosmetiques.affiche();
  2. La seconde pour extraire le filtre du champ de texte "filtre".

La première de ces modifications se résume à changer la fin du script en:

var cosmetiques = new Cosmetiques();
document.getElementById("affiche").onclick = function() {      
    cosmetiques.affiche();
};

Cela revient à dire "trouver l'élément id 'affiche' du document, et exécuter cosmetique.affiche() quand on clique dessus".

La seconde de nos modifications consiste à extraire le critère de sélection du champ "filtre" plutôt que de l'URL:

var filtre      = document.getElementById("filtre").value;

Au final le code complet devient:

document.writeln("<form action='#'>");
document.writeln("<input type='text' id='filtre' />");
document.writeln("<input type='button' value='Chercher' id='affiche' />");
document.writeln("</form>");
 
var Cosmetiques = function() {
    var     that = {};
 
    that.affiche = function() {
        var liste       = document.getElementById("produits");
        var items       = liste.getElementsByTagName("li");
 
        var filtre      = document.getElementById("filtre").value;
 
        for(i = 0; i < items.length; i = i+1) {
            var label = items[i].firstChild.nodeValue;
            if ((filtre.length < 1) || (label.indexOf(filtre.substring(1)) != -1))
                items[i].style.display = "list-item";
            else
                items[i].style.display = "none";
        }
    }
 
    return that;
};
 
var cosmetiques = new Cosmetiques();
document.getElementById("affiche").onclick = function() {
    cosmetiques.affiche();
};

Dorénavant, une fois la page chargée par le navigateur, chaque 'clic' sur le boutton "Chercher" affichera les produits correspondant au critère de recherche sans avoir besoin de recharger la page.

A vous de jouer

A la fin de cet article, non seulement vous avez vu comment utiliser l'expression conditionnelle if ... else ..., mais vous avez aussi eu l'occasion de découvrir comment manipuler des documents HTML et utiliser des formulaires en PHP et JavaScript.

Nous allons mettre tout cela en pratique dans ce "A vous de jouer"

Server side

  1. Ecrivez un programme en PHP pour présenter à l'utilisateur une liste de film de science fiction des années 80 que vous trouverez à l'adresse http://en.wikipedia.org/wiki/List_of_science_fiction_films:_1980s.
  2. Faites-en sorte que l'année de parution du film soit également affichée – mais en italique (en utilisant l'élément HTML <em>...</em>);
  3. Permettez à l'utilisateur de filtrer le résultat obtenu en fonction du titre;
  4. Permettez à l'utilisateur de filtrer le résultat obtenu en fonction de l'année.

Client side

Afin de rendre le résultat plus lisible, on souhaite alterner la couleur de fond des lignes de résultat obtenu de la manière suivante:

Tron (1982)
Return of the Jedi (1983)
The Adventures of Buckaroo Banzai Across the 8th Dimension (1984)
Dune (1984)
The Terminator (1984)
...