Tutorials

Autocompletamento dei campi input text con javascript e ajax


Questa guida è basata sul tutorial che ho preso da qui e il cui autore è Nicholas C. Zakas.


L' ispirazione è stata data inizialmente da google con il suo google suggest che utilizza tra l'altro ajax per tenere aggiornati i "suggerimenti" interrogando in maniera asincrona un loro server man mano che voi scrivete la parola.
In questa guida vedremo come realizzare un campo <input type="text"/> che si comporterà in questo modo:

Per ottenere tutto questo sarà necessario utilizzare javascript (programmazione ad oggetti) e scrivere qualche riga nel foglio di stile della pagina.
Se volete utilizzare il codice per implementare questo oggetto velocemente potete andare direttamente al metodo veloce, se invece volete vedere come viene realizzato l'autocompletamento, potete partire da qui:

Metodo Lento

Come potete notare, quando scrivete qualcosa nel campo input (per es 'b'), la parola viene automaticamente completata con il primo suggerimento trovato ('baldassarre'), selezionando la parte aggiunta ('aldassarre'):

Comincieremo creando il codice necessario per fare questa operazione.

Il programma sarà costituito da due entità, la classe principale che chiameremo AutoSuggestControl e una classe di utilità che fornirà i suggerimenti ad AutoSuggestControl e che chiameremo StateSuggestions
Intanto creiamo la classe AutoSuggestControl. Per inizializzarla dovremo passargli la textbox che avrà l'autocompletamento, e un istanza della classe StateSuggestions:
function AutoSuggestControl(oTextbox, oProvider) {     this.provider = oProvider;     this.textbox = oTextbox;     this.cur = -1;     this.layer = null;     this.init(); }
Oltre al riferimento alla textbox e all'istanza del suggeritore, ci sarà utile un indice (cur) per gestire la posizione del focus nella lista dropdown, un riferimento chiamato layer in cui salveremo il riferimento al <div> per creare la lista dropdown ed infine c'è la chiamata al metodo init() che inizializzerà l'oggetto.

Selezione del testo

Adesso creiamo il metodo della classe AutoSuggestControl per evidenziare la porzione della parola suggerita. Il metodo avrà come parametri la posizione del carattere di partenza e il numero dei caratteri da evidenziare nella textbox:
AutoSuggestControl.prototype.selectRange = function (iStart, iLength) {     if (this.textbox.createTextRange) { // metodo per Explorer         var oRange = this.textbox.createTextRange();         oRange.moveStart("character", iStart);
        oRange.moveEnd("character", iLength - this.textbox.value.length);         oRange.select();     } else if (this.textbox.setSelectionRange) { // metodo per Mozilla         this.textbox.setSelectionRange(iStart, iLength);     }     this.textbox.focus(); };

Autocompletamento

Adesso che siamo in grado di selezionare una parte di parola, creaiamo il metodo che scrive nella textbox la parte rimanente della parola suggerita.
Il metodo avrà come parametro la parola suggerita.
AutoSuggestControl.prototype.typeAhead = function (sSuggestion) {     if (this.textbox.createTextRange || this.textbox.setSelectionRange) {         var iLen = this.textbox.value.length;         this.textbox.value = sSuggestion;         this.selectRange(iLen, sSuggestion.length);     } };
nell'ordine questo metodo
- salva la lunghezza della parola scritta dall'utente
- scrive nella textbox la parola suggerita

il metodo autosuggest()

Questo metodo riceverà un array di stringhe contenente tutte le parole suggerite e un booleano. Il parametro booleano serve per sapere se mostrare o no la lista dropdown
Controlla che l'array non sia vuoto, se è vuoto nasconde la lista dropdown, altrimenti chiama la funzione typeAhead per scrivere nella text box la prima parola conenuta nell'array.
AutoSuggestControl.prototype.autosuggest = function(aSuggestions, bTypeAhead) {     if (aSuggestions.length > 0) {         if (bTypeAhead) {             this.typeAhead(aSuggestions[0]);         }         this.showSuggestions(aSuggestions);     } else {         this.hideSuggestions();     } };

La gestione degli eventi

Dovremo gestire i tasti premuti sulla tastiera, controllare quale tasto è stato premuto e agire di conseguenza. Se per esempio viene premuto SHIFT non dobbiamo fare nulla, e questo vale per tutta una serie di tasti, qua sotto potete vedere i tasti che non gestiremo con il relativo codice:
Key Code Key Code
Backspace 8 Print Screen 44
Tab 9 Delete 46
Enter 13 F1 112
Shift 16 F2 113
Ctrl 17 F3 114
Alt 18 F4 115
Pause/Break 19 F5 116
Caps Lock 20 F6 117
Esc 27 F7 118
Page Up 33 F8 119
Page Down 34 F9 120
End 35 F10 121
Home 36 F11 122
Left Arrow 37 F12 123
Up Arrow 38    
Right Arrow 39    
Down Arrow 40    
Quindi i tasti qui sopra non li gestiremo, mentre gestiremo i tasti con codice 8 e 46 (Backspace e Delete) mostrando la lista dropdown ma senza modificare la parola, chiameremo quindi requestSuggestions con il secondo parametro a false, che, ricordo, serve proprio a evitare che venga eseguito il metodo typeAhead. Per tutti gli altri tasti chiameremo requestSuggestions lasciando anche che venga eseguito typeAhead:
AutoSuggestControl.prototype.handleKeyUp = function (oEvent) {     var iKeyCode = oEvent.keyCode;     if (iKeyCode == 8 || iKeyCode == 46) {         this.provider.requestSuggestions(this, false);     } else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {         //ignore     } else {         //this.provider.requestSuggestions(this);         this.provider.requestSuggestions(this, true);     } };

Il fornitore dei suggerimenti

Possiamo ora scrivere la classe che suggerirà le parole, ovvero SuggestionProvider. Essa avrà un metodo, requestSuggestion() che fornirà ad AutoSuggestControl una lista di parole. Inoltre in questa classe salveremo la lista delle parole tra cui cercare i suggerimenti.
function StateSuggestions() {     this.states = [     "Abelardo","Antonio","Amilcare","Aida",     "Baldassarre","Beatrice","Benedetta","Benito","Berto","Bruno",     "Caio","Calimero","Calliope","Callimaco",     "Dafne","Dante","Danilo","Davide","Denis","Domenico",     "Emilio","Ernesto","Evaristo",     "Fabio","Francesco","Federico","Ferruccio",     "Giacomo","Giuseppe","Gianfranco","Gigi","Geronimo",     "Luigi","Libero","Laura","Loretta","Luca",     "Mario","Michele","Mauro","Mattia",     "Nicola","Nino","Nivea",     "Oreste","Olga","Olimpia","Oscar","Osvaldo","Omar",     "Pippo","Peppino","Pasquale","Patrizia","Piero",     "Quarto","Quirino",     "Roberta","Roberto","Romeo","Remigio","Rosanna","Raffaele",     "Sabina","Saverio","Silvano","Silvio","Simone","Sempronio",     "Tizio","Tiziano","Tullio",     "Ubaldo","Ugo","Umberto","Ulisse",     "Veronica","Vinicio","Valentina","Valentino","Virgilio","Vladimiro",     "Walter","Wanda","Wilma",     "Zenone","Zaccaria"     ]; }
Ma la parte interessante è il metodo requestSuggestions:
come parametri accetterà il reference al controllore (per richiamare la sua funzione autosuggest) più il booleano per eseguire o meno il typeAhead:
StateSuggestions.prototype.requestSuggestions =   function (oAutoSuggestControl /*:AutoSuggestControl*/, bTypeAhead) {     var aSuggestions = [];     var sTextboxValue = oAutoSuggestControl.textbox.value;     if (sTextboxValue.length > 0){         // trasformo tutto in minuscolo         var sTextboxValueLC = sTextboxValue.toLowerCase();         //cerco le parole che combaciano         for (var i=0; i < this.states.length; i++) {             // trasformo anche i suggerimenti in minuscolo             var sStateLC = this.states[i].toLowerCase();             if (sStateLC.indexOf(sTextboxValueLC) == 0) {              aSuggestions.push(sTextboxValue + this.states[i].substring(sTextboxValue.length));             }         }     }     //fornisco i suggerimenti al controllore AutoSuggestControl     oAutoSuggestControl.autosuggest(aSuggestions, bTypeAhead); };

Fino a questo punto abbiamo creato le funzionalità per la ricerca delle parole e il typeAhead, ora vediamo come creare la lista con tutte le parole suggerite.

La lista dropDown


La lista che compare sotto la nostra text box, non è altro che un div con uno stile adatto e posto esattamente sotto la text box. Vedremo che per calcolare la posizione esatta non sarà così semplice.


Questo, intanto, è lo stile del conenitore dei suggerimenti, il box:
div.suggestions {     -moz-box-sizing: border-box;     box-sizing: border-box;     border: 1px solid black;     position: absolute; }
Mentre questo è lo stile per i singoli elementi, le stringhe da suggerire:
div.suggestions div {     cursor: default;     padding: 0px 3px; }
Infine questo è lo stile per l'elemento selezionato:
div.suggestions div.current {     background-color: #3366cc;     color: white; }

In questa parte della guida andrò più veloce poichè la maggiorparte sono metodi che interagiscono con il DOM del documento

Ora vediamo di implementare intanto il metodo per nascondere la lista dropDown, a questo scopo modificheremo lo stile del membro "layer" della classe AutoSuggestControl, che corrisponderà al DIV del box contenente i suggerimenti.
AutoSuggestControl.prototype.hideSuggestions = function () {     this.layer.style.visibility = "hidden"; };
Vediamo ora come evidenziare uno degli elementi della lista:
AutoSuggestControl.prototype.highlightSuggestion =   function (oSuggestionNode) {     for (var i=0; i < this.layer.childNodes.length; i++) {         var oNode = this.layer.childNodes[i];         if (oNode == oSuggestionNode) {             oNode.className = "current"         } else if (oNode.className == "current") {             oNode.className = "";         }     } };


Ora è arrivato il momento di creare la lista dropdown con il DIV. Il metodo createDropDown, oltre ad inserire i DIV nel documento, registrerò i gestori degli eventi per la lista.
AutoSuggestControl.prototype.createDropDown = function () {     this.layer = document.createElement("div");     this.layer.className = "suggestions";     this.layer.style.visibility = "hidden";     this.layer.style.width = this.textbox.offsetWidth;     document.body.appendChild(this.layer);     // gestione mouse     var oThis = this;     this.layer.onmousedown = this.layer.onmouseup =     this.layer.onmouseover = function (oEvent) {         oEvent = oEvent || window.event;         oTarget = oEvent.target || oEvent.srcElement;         if (oEvent.type == "mousedown") {             oThis.textbox.value = oTarget.firstChild.nodeValue;             oThis.hideSuggestions();         } else if (oEvent.type == "mouseover") {             oThis.highlightSuggestion(oTarget);         } else { // mouseup             oThis.textbox.focus();         }     }; };


Dobbiamo ora posizionare la lista nel punto esatto, sotto il text box, per farlo sembrare una vera dropdown menu.
Per farlo dobbiamo trovare le coordinate del punto in basso a sinistra del box, e calcolarne la larghezza.
Scriviamo due metodi che ci aiutano nell' arduo compito:
AutoSuggestControl.prototype.getLeft = function () {     var oNode = this.textbox;     var iLeft = 0;     while(oNode.tagName != "BODY") {         iLeft += oNode.offsetLeft;         oNode = oNode.offsetParent;     }     return iLeft; }; AutoSuggestControl.prototype.getTop = function () {     var oNode = this.textbox;     var iTop = 0;     while(oNode.tagName != "BODY") {         iTop += oNode.offsetTop;         oNode = oNode.offsetParent;     }     return iTop; };
I due metodi risalgono di nodo in nodo, dalla textbox al nodo "BODY", prendendo l'offset sinistro (o alto) rispetto al nodo superiore, e sommandoli ogni volta.
Alla fine del ciclo avremo in pixel la distanza della textbox dal bordo sinistro e alto della pagina.

Usiamo i due metodi appena visti per far apparire la lista nel punto esatto creando questo metodo che accetta una lista di suggerimenti e li mostra nel layer, in pratica crea un div dentro a layer per ogni suggerimento e ci scrive dentro la parola:
AutoSuggestControl.prototype.showSuggestions = function (aSuggestions) {     var oDiv = null;     this.layer.innerHTML = "";     for (var i=0; i < aSuggestions.length; i++) {         oDiv = document.createElement("div");         oDiv.appendChild(document.createTextNode(aSuggestions[i]));         this.layer.appendChild(oDiv);     }     this.layer.style.left = this.getLeft() + "px";     this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px";     this.layer.style.visibility = "visible"; };


Possiamo pensare ora alla gestione degli eventi della tastiera, come l'uso delle freccie per muoversi in alto e in basso nella lista dropdown.
A questo scopo creiamo i metodi che ci permettono di navigare nella lista:
AutoSuggestControl.prototype.nextSuggestion = function () {     var cSuggestionNodes = this.layer.childNodes;     if (cSuggestionNodes.length > 0 && this.cur < cSuggestionNodes.length-1) {         var oNode = cSuggestionNodes[++this.cur];         this.highlightSuggestion(oNode);         this.textbox.value = oNode.firstChild.nodeValue;     } }; AutoSuggestControl.prototype.previousSuggestion = function () {     var cSuggestionNodes = this.layer.childNodes;     if (cSuggestionNodes.length > 0 && this.cur > 0) {         var oNode = cSuggestionNodes[--this.cur];         this.highlightSuggestion(oNode);         this.textbox.value = oNode.firstChild.nodeValue;     } };


Per gestire i tasti su e giu dobbiamo creare un metodo che gestirà questi due eventi e richiamerà i metodi appena scritti per navigare nella lista
// funzione che verrà bindata per gestire gli eventi key arrow AutoSuggestControl.prototype.handleKeyDown = function (oEvent) {     switch(oEvent.keyCode) {         case 38: //up arrow             this.previousSuggestion();             break;         case 40: //down arrow             this.nextSuggestion();             break;         case 13: //enter             this.hideSuggestions();             break;     } };


Abbiamo finalmente finito di scrivere i metodi funzionali alla nostra applicazione, ma dobbiamo ancora inizializzarli, questo ci permetterà di legare gli eventi della tastiera con i metodi di gestione che abbiamo scritto. Ecco quindi il metodo init:
AutoSuggestControl.prototype.init = function () {     var oThis = this;     //quando si verifica un evento onkeyup, esegui questa funzione     //passandogli l'oggetto oEvent che è una cosa propria del DOM:     this.textbox.onkeyup = function (oEvent) {         // può essere che con Explorer non ci sia l'oggett oEvent,         // al suo posto viene utilizzato l'oggetto event della classe window:         if (!oEvent) {             oEvent = window.event;         }         // assegno a handleKeyUp la gestione dell'evento onkeyUp         oThis.handleKeyUp(oEvent);     };     // per gestire le freccie e l'enter per il menu a tendina     this.textbox.onkeydown = function (oEvent) {         if (!oEvent) {             oEvent = window.event;         }         oThis.handleKeyDown(oEvent);     };     // per gestire la perdita del focus, dovrò nascondere la tendina     this.textbox.onblur = function () {         oThis.hideSuggestions();     };     this.createDropDown(); };


Si conclude così la guida per la creazione di un text box con l'autocompletamento e con l'autosuggeritore.
Per provarlo da voi leggete ancora il metodo veloce qui sotto, dove trovate anche il file javascript con tutto il codice necessario.




Metodo Veloce

Prendete il codice javascript da qui che dovrete includere nella vostra pagina, per esempio scrivendo nell'header qualcosa come
<script type="text/javascript" src="/scripts/autocompletamento.js"></script>
Aggiungete ancora questo script nell'header della pagina che avrà la text box:
<script type="text/javascript"> window.onload = function () { var oTextbox = new AutoSuggestControl(document.getElementById("txt1"), new StateSuggestions()); } </script>
Inserite nel vostro foglio di stile CSS queste righe:
div.suggestions {     -moz-box-sizing: border-box;     /*box-sizing: border-box;*/     border: 1px solid black;     position: absolute; } div.suggestions div {     cursor: default;     padding: 0px 3px;     background-color: #eef; } div.suggestions div.current {     background-color: #6580D8;     color: white; }
a questo punto dovete creare un campo input text con id=txt1:
<input id="txt1" type="text"/>
Per modificare le parole suggerite aprite il file javascript, troverete un array con tutte le parole possibili, vi basterà modificare quell'array scrivendo le parole che volete che vi vengano suggerite.



Se trovate degli errori nel codice o qualche malfunzionamento non esitate a scrivermi alla mail che trovate nella sezione contatti.