Autocomplete widget

Creare un campo di testo che si autocompleti non è banale, presuppone eseguire delle query ad un db ogni volta che l'utente digita un carattere sulla tastiere. Ma andiamo per ordine:

Prima di tutto ci serve un campo di input:

<form id="search" action="search/index.php" method="get">
<label for="search-text">search the site</label>
<input type="text" name="search-text" id="search-text" />
</form>

Inoltre ci serve il file php cui inviare il valore del nostro campo di testo e da cui recuperare i match nel database:


<?php
if (strlen($_REQUEST['search-text']) < 1) {
print '[]';
exit;
}

$terms = array( 'access', 'action', 'active', 'step-by-step', 'stores', 'struts', 'sugarcrm', 'sysadmins', 'systems', 'tcp/ip', 'techniques', 'telefonia', 'telefonici', 'telefónicos', 'telephony' ); $possibilities = array(); foreach ($terms as $term) { if (strpos($term, strtolower($_REQUEST['search-text'])) === 0) { $possibilities[] = "'". str_replace("'", "\\'", $term) ."'";
} } print ('['. implode(', ', $possibilities) .']');

Analizziamo il php:

Proviamo a cercare un termine: autocomplete step 1



E se voglio chiamare il file php con AJAX?

Javascript code:

$(document).ready(function() {
	var $autocomplete = $('<ul class="autocomplete"></ul>').hide().
	insertAfter('#search-text');

	$('#search-text').keyup(function() {
		$.ajax({
		'url': 'search/index.php',
		'data': {'search-text': $('#search-text').val()},
		'dataType': 'json',
		'type': 'POST',
		'success': function(data) {
			if (data.length) {
				$autocomplete.empty();
				$.each(data, function(index, term) {
					$('<li></li>').text(term).appendTo($autocomplete);
				});
			$autocomplete.show();
			}
		}
	});
});
});

Analizziamo il codice:

Riproviamo adesso: autocomplete step 2



Adesso dobbiamo rendere cliccabile gli items della lista ed inseriri il valore selezionato nel campo di input:

Javascript code:

[...]

.appendTo($autocomplete).click(function() {
	$('#search-text').val(term);
	$autocomplete.hide();
});

Ed ecco il risultato: autocomplete step 3



Adesso evidenziamo il primo risultato e l'item correntemente sotto al mouse.

Per fare ciò abbiamo sempre bisogno di sapere l'item selezionato o su cui siamo sopra (il suo indice insomma)

Per fare ciò scriveremo una funzione:

Javascript code:

var selectedItem = null;
var setSelectedItem = function(item) {
selectedItem = item;

if (selectedItem === null) {
	$autocomplete.hide();
	return;
}

if (selectedItem < 0) {
	selectedItem = 0;
}

if (selectedItem >= $autocomplete.find('li').length) {
	selectedItem = $autocomplete.find('li').length - 1;
}

$autocomplete.find('li').removeClass('selected').eq(selectedItem).addClass('selected');
$autocomplete.show();
};

Analizziamo il codice:

Qualche ulteriore aggiustamento al codice:

$('#search-text').attr('autocomplete', 'off').keyup(function() {
	$.ajax({
    'url': 'search/index.php',
    'data': {'search-text': $('#search-text').val()},
    'dataType': 'json',
    'type': 'POST',
    'success': function(data) {
		if (data.length) {
$autocomplete.empty();
$.each(data, function(index, term) { $('<li></li>').text(term) .appendTo($autocomplete).mouseover(function() {
setSelectedItem(index);
}).click(function() { $('#search-text').val(term); $autocomplete.hide(); }); }); setSelectedItem(0);
} else { setSelectedItem(null); } } }); });

Ed ecco il risultato: autocomplete step 4



Adesso vogliamo aggiungere interattività con la tastiera. Cioè fare in modo di poter scorrere la lista con le arrow key up e down

Javascript code:

$('#search-text').attr('autocomplete', 'off').keyup(function(event) {
if (event.keyCode > 40 || event.keyCode == 8) { // Keys with codes 40 and below are special // (enter, arrow keys, escape, etc.). // Key code 8 is backspace. $.ajax({ 'url': 'search/index.php', 'data': {'search-text': $('#search-text').val()}, 'dataType': 'json',
'type': 'POST',
'success': function(data) {
if (data.length) {
$autocomplete.empty();
$.each(data, function(index, term) {
$('<li></li>').text(term)
.appendTo($autocomplete).mouseover(function() {
setSelectedItem(index);
}).click(function() {
$('#search-text').val(term);
$autocomplete.hide();
}); }); setSelectedItem(0); } else { setSelectedItem(null); } } }); } else if (event.keyCode == 38 && selectedItem !== null) {
// User pressed up arrow setSelectedItem(selectedItem - 1); event.preventDefault(); } else if (event.keyCode == 40 && selectedItem !== null) { // User pressed down arrow. setSelectedItem(selectedItem + 1); event.preventDefault(); } });

In questo caso il codice è rimasto invariato. Abbiamo solo specificato le condizioni rispetto a quali tasti prma l'utente. Nel caso l'utente prma le frecce, chiamiamo ogni volta la funzione setSelectedItem che ci siamo scritti

Ed ecco il risultato finale: autocomplete step 5