Kommunauty
Connexion
Inscription

Javascript : réaliser un champ de texte avec auto-suggestions

le 10 septembre 2011 • Programmation • par Lucas

Vous aimeriez pouvoir réaliser une barre de recherche avec des propositions dynamiques à la manière de Google, mais vous ne savez pas comment vous y prendre ?

Alors suivez-moi ! Nous allons voir ensemble tout du long de ce tutoriel comment réaliser ce type de champ de texte !

Sommaire

Présentation

Le principe est simple : nous voulons réaliser un champ de texte qui, lorsque le visiteur saisira une lettre, ira chercher dans une liste de mots clés ceux qui commencent (ou contiennent) ces lettres, pour les afficher au visiteur.

Voir la démo

(essayez avec "K","k", "B", "S"...)

Préparation

Bon, nous n'allons pas du tout nous occuper du design du champ de texte dans ce tutoriel, donc je vous donne la structure HTML qu'aura le champ de texte avec les suggestions, et un design que j'ai intégré à partir de ce PSD :

<form id="auto-suggest" action="#" method="post">
    <input type="text" class="search" name="search" value="Rechercher..." onfocus="if(this.value=='Rechercher...')this.value=''" onblur="if(this.value=='')this.value='Rechercher...'" autocomplete="off"/>
    <ul class="suggestions">
        <li>Suggestion 1</li>
        <li>Suggestion 2</li>
        <li>Suggestion 3</li>
        <li>Suggestion 4</li>
        <li>Suggestion 5</li>
    </ul>
</form>

le CSS :

Afficher le CSS
Fermer ce cadre

html, body {background:url(images/noise.png) left top repeat}
#auto-suggest {width:358px;margin:0 auto}
#auto-suggest .search {
width:322px;height:33px;margin:4px;padding:0 13px;border:1px solid #cdcdcd;
color:#ccc;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:14px;font-weight:bold;
-moz-border-radius:2px;
-webkit-border-radius:2px;
border-radius:2px;
-moz-box-shadow:inset 0 1px 4px rgba(0,0,0,0.15), 4px 4px 0 #f1f1f1,-4px 4px 0 #f1f1f1,-4px -4px 0 #f1f1f1,4px -4px 0 #f1f1f1;
-webkit-box-shadow:inset 0 1px 4px rgba(0,0,0,0.15), 4px 4px 0 #f1f1f1,-4px 4px 0 #f1f1f1,-4px -4px 0 #f1f1f1,4px -4px 0 #f1f1f1;
box-shadow:inset 0 1px 4px rgba(0,0,0,0.15), 4px 4px 0 #f1f1f1,-4px 4px 0 #f1f1f1,-4px -4px 0 #f1f1f1,4px -4px 0 #f1f1f1
}

#auto-suggest .search:focus {
color:#555;border-color:#c8c8c8;
-moz-box-shadow:inset 0 1px 4px rgba(0,0,0,0.15), 4px 4px 0 #ececec,-4px 4px 0 #ececec,-4px -4px 0 #ececec,4px -4px 0 #ececec;
-webkit-box-shadow:inset 0 1px 4px rgba(0,0,0,0.15), 4px 4px 0 #ececec,-4px 4px 0 #ececec,-4px -4px 0 #ececec,4px -4px 0 #ececec;
box-shadow:inset 0 1px 4px rgba(0,0,0,0.15), 4px 4px 0 #ececec,-4px 4px 0 #ececec,-4px -4px 0 #ececec,4px -4px 0 #ececec
}

#auto-suggest .suggestions {
width:342px;position:relative;margin:-6px auto 0;padding:0;list-style-type:none;border:1px solid #d5d4d4;background:#fff;
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:13px;color:#555;
-moz-border-radius:2px;
-webkit-border-radius:2px;
border-radius:2px;
-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);
-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);
box-shadow:0 1px 3px rgba(0,0,0,0.1)
}

#auto-suggest .suggestions li {height:25px;padding:0 10px;line-height:25px;cursorointer;border-top:1px solid #f5f5f5}
#auto-suggest .suggestions li:hover {background:url(images/tick.png) no-repeat #fffac2;background-position:320px center;border-top-color:#fffac2}
#auto-suggest .suggestions li:first-child {border:none;}

et les images placées dans un dossier "images" :

tick.png

noise.png

Bon, comme vous pouvez le constater, l'intégration n'est pas parfaite, mais ça suffira amplement .

N'oubliez pas la ligne

autocomplete="off"

dans le champ de texte qui empêche les suggestions du navigateur, sinon c'est pas beau


Permettez-moi de revenir sur une ligne de code donnée dans le HTML :

<input type="text" class="search" name="search" value="Rechercher..." onfocus="if(this.value=='Rechercher...')this.value=''" onblur="if(this.value=='')this.value='Rechercher...'"/>

Comme vous pouvez le constater, ce champ de texte contient du javascript.

En fait lorsque l'utilisateur clique sur le champ de texte, la page appelle l'évènement "onfocus" du champ, qui contient :

if(this.value=='Rechercher...')this.value=''

En gros, si la valeur du champ est égale à 'Rechercher...' (la valeur par défaut), on vide le champ de texte, ce qui évite à l'utilisateur de devoir lui-même effacer le mot, qui compte comme du texte entré par le visiteur.

Pareillement, lorsque le visiteur clique en dehors de la page pour enlever son curseur du champ, l'évènement "onblur" est appelé :

if(this.value=='')this.value='Rechercher...'

On regarde si le contenu du champ est vide, auquel cas on modifie la valeur en 'Rechercher...', pour que le visiteur, qui finalement n'a rien inscrit dans le champ, se souvienne de son utilité .


Bien, et maintenant la touche finale :

créez un fichier script.js et ajoutez cette ligne entre les balises <head></head> de votre page HTML :

<script type="text/javascript" src="script.js"></script>

Codage !!

Allez zou ! c'est parti !

Bon, rappelez-vous de ce que j'avais dit dans le précédent tutoriel Permettre les tabulations dans une zone de texte : pour récupérer des infos sur un élément de la page en Javascript, il faut impérativement qu'elle ai fini de charger.

Donc pour exécuter le code après le chargement de la page 2 solutions :

  • Mettre la balise <script> à la fin du BODY (sâle)
  • Mettre le code javascript dans un window.onload (clean )

Nous allons donc utiliser l'évènement window.onload

Donc, nous en sommes à ce stade :

window.onload = function(){

};

c'est parti pour le code :

// Tout d'abord créons un tableau de mots clefs :
var motsClefs = [
'Kommunauty',
'Kommunauté',
'K',
'Krousty',
'Bob',
'Boby',
'Bobu'
        // ...
];

// On récupère le formulaire à l'aide de son ID
var form = document.getElementById("auto-suggest");
// On récupère le champ de texte
var input = form.search;

Bon, je me dois de revenir sur cette ligne :

var input = form.search;

Sachez qu'en javascript, on peut obtenir un élément du HTML grace à son attribut "name" en faisant :

nomDuConteneur(ici le formulaire).attributNameDeLElement


Ensuite, vous pouvez supprimer la liste de suggestions de votre Code HTML car nous allons la créer nous-même dynamiquement en javascript  :

// On créer un "UL"
var list = document.createElement("ul");
// On lui ajoute la classe "suggestions"
list.className = "suggestions";
// On le cache pour qu'il ne soit pas visible
list.style.display = "none";

// On l'ajoute à l'intérieur du formulaire
form.appendChild(list);

Maintenant, si vous regardez le code source de votre page avec Firebug, vous devriez voir un "UL" ayant la classe "suggestions" dissimulé dans le formulaire.


Bon, nous voulons que les suggestions apparaissent uniquement si l'utilisateur tape dans le champ de texte, et uniquement s'il n'est pas vide.

Nous allons donc utiliser l'évènement onkeyup du champ de texte.

Pourquoi onkeyup (Quand on relâche la touche) plutôt que onkeydown (Quand on enfonce la touche du clavier) ?

Et bien tout simplement parce que onkeydown est appelé juste au moment où la touche est enfoncée, et donc juste AVANT que la lettre soit ajoutée au champ de texte.

c'est parti !!

input.onkeyup = function(){
    // On récupère le texte
    var txt = this.value;

    // Si le texte est vide, alors on arrète la la fonction, et on cache la liste
    if(!txt){
        list.style.display = "none";
return;
    }
    
    // variable qui indiquera le nombre de suggestions correspondantes
    var suggestions = 0;
    // On créer une variable qui contiendra toute les suggestions qui seront affichées
    var frag = document.createDocumentFragment();

    for(var i = 0, c = motsClefs.length; i < c; i++){
        // le code qui va venir après
    }

    // Si il y a des suggestions qui peuvent être affichées
    if(suggestions){
        // On vide le contenu de la liste
        list.innerHTML = "";
        // On lui ajoute les différentes suggestions
list.appendChild(frag);
        // On affiche la liste
list.style.display = "block";
    } // Sinon s'il n'y en a pas
    else {
        // On cache la liste
list.style.display = "none";
    }
};

// Quand l'utilisateur dé-sélectionne le champ
// (Quand il clique ailleurs sur la page)
input.onblur = function(){
    // On cache la liste
    list.style.display = "none";
    // Si le contenu est vide on le remplace par le mot par défaut
    if(this.value=="")
        this.value = "Rechercher...";
};

Je vais à présent revenir sur cette ligne :

var frag = document.createDocumentFragment();

En fait, nous créons un "Fragment", qui contiendra les suggestions.

C'est ce... cette... ce "truc" qui sera lui-même ajouté dans la liste.

Son intérêt : plutôt que d'ajouter dans la boucle "for" les suggestions une à une dans la liste, ce qui consomme plus de ressources, on les stocke toutes dans une seule variable, pour les afficher toutes en même temps

--> Plus rapide


Notez qu'il faut supprimer l'évènement onBlur qu'on avait ajouté au champ de texte dans le HTML, puisqu'il est "écrasé" par l'évènement onBlur du script :

input.onblur = function(){
    // On cache la liste
    list.style.display = "none";
    // Si le contenu est vide on le remplace par le mot par défaut
    if(this.value=="")
        this.value = "Rechercher...";
};


Bon, attaquons nous maintenant à la boucle for, qui parcourt tous les mots-clefs.

Nous allons utiliser les RegEx, les Expressions régulières qui permettent de tester des expressions dans un texte.

Les Regex peuvent être créée de 2 manières différentes :

  • La Longue :

    var reg = new RegExp("l'expression régulière");
  • La Courte :

    var reg = /l'expression régulière/;

Nous seront néamoins contraint pour notre utilisation à la Regex longue, parce que l'on pourra insérer des variable dans l'expression :

var reg = new RegExp("debut"+masupervariable+"fin");


Bien ! Avant de coder, construisons ensemble la Regex :

On veut savoir si le mot clef commence par les lettres entrées, sans respecter la casse, donc :

^lesLettresEntrées

pour ne pas respecter la casse, sachez que l'objet Regex prend un 2ème paramètre, donc :

var reg = new RegExp("^"+txt, "i");

("i" signifie Insensitive)


Voilà ! je crois que tout est dit, on ajoute ça au code dans la boucle  :


// Si l'expression régulière correspond au mot clef
if(new RegExp("^"+txt,"i").test(motsClefs[i])){
    // On créé l'élément HTML "LI"
    var word = document.createElement("li");
    // On l'ajoute au fragment
    frag.appendChild(word);
    // On lui mets comme contenu le mot clef avec en gras les lettres entrées
    // (grâce à une RegEx et à la fonction replace() ^^ )
    word.innerHTML = motsClefs[i].replace(new RegExp("^("+txt+")","i"),"<strong>$1</strong>");
    // On ajoute le mot clef à l'élément HTML "LI", pour pouvoir le récupérer plus tard
    word.mot = motsClefs[i];

    // Lorsque le visiteur clique sur le lien
    word.onmousedown = function(){
        // On re-sélectionne le champ de texte
input.focus();
        // On remplace sa valeur par le mot clef
input.value = this.mot;
        // On cache la liste
list.style.display = "none";

        // On empêche la dé-sélection du champ
        return false;
    };
    // On indique qu'une suggestion a été ajoutée
    suggestions++;
}

Améliorations

Bon, les enfants, histoire que vous ne chaumiez pas pendant votre temps libre, essayez d'améliorer le script.

  • Quand il y a une petite liste de mots clefs, c'est fluide, mais quand on en a beaucoup, ça devient vite galère, donc essayez de faire un système qui une fois la première lettre entrée stocke dans un deuxième tableau seulement les mots commençant par cette lettre, histoire d'alléger la boucle for quand juste une autre lettre est tapée.


Voilà ! ce tutoriel arrive à son terme ! j'espère que vous avez appris de nouvelles choses à propos du javascript et que ce tutoriel vous aura été utile !

N'hésitez pas à commenter.

Ah oui !

le récapitulatif pour les fainéants : (j'oubliais que même sur K y a encore des gens qui copient tout )

Récapitulatif du code (non commenté)
Fermer ce cadre

window.onload = function(){
var motsClefs = [
'Kommunauty',
'Kommunauté',
'K',
'Krousty',
'Bob',
'Boby',
'Bobu'
];

var form = document.getElementById("auto-suggest");
var input = form.search;

var list = document.createElement("ul");
list.className = "suggestions";
list.style.display = "none";

form.appendChild(list);

input.onkeyup = function(){
var txt = this.value;
if(!txt){
list.style.display = "none";
    return;
}

var suggestions = 0;
var frag = document.createDocumentFragment();

for(var i = 0, c = motsClefs.length; i < c; i++){
if(new RegExp("^"+txt,"i").test(motsClefs[i])){
var word = document.createElement("li");
frag.appendChild(word);
word.innerHTML = motsClefs[i].replace(new RegExp("^("+txt+")","i"),"<strong>$1</strong>");
word.mot = motsClefs[i];
word.onmousedown = function(){
input.focus();
input.value = this.mot;
list.style.display = "none";
return false;
};
suggestions++;
}
}

if(suggestions){
list.innerHTML = "";
list.appendChild(frag);
list.style.display = "block";
}
else {
list.style.display = "none";
}
};

input.onblur = function(){
list.style.display = "none";
        if(this.value=="")
            this.value = "Rechercher...";
};
};

Lucas.

Ps : nous sommes TOUJOURS des rebelles

  
26 commentaires Afficher les commentaires

Ajoute un commentaire !

Ajouter une image... Trouvée sur internet » De mon PC »
Adresse URL :
Adresse de la page de la vidéo :
Taille du texte :
Couleur du texte :

Article lu 25014 fois.