Hallo!
Je kent het wel, je zit lekker in je admin panel te kloten en zoekt naar een user maar je weet zijn username/ID niet helemaal ? Of iemand die een onmogelijke niet te onthouden username heeft, wat doe je dan ? Idd, lang zoeken..
AJAX kan dit veel tijd besparen, misschien wel eens gezien, een dropdown als je begint met typen ? Dat is wat ik ga uitleggen hoe dat te maken is, onderaan zal een werkend voorbeeld zijn
Je hebt een basis JavaScript kennis nodig + Je moet weten hoe je met een database in PHP omgaat.
Ok, wat we gaan doen:
- Database aanmaken + testwaarden erin zetten
- HTML formulier aanmaken
- De autocomplete class aanmaken
- Uitleg over de class
- Class schrijven
- ajax.php aanmaken
- Uitleg wat er in ajax.php gebeuren moet
- ajax.php schrijven
- Testen
Database aanmaken
Maak een database aan genaamd:
autocomplete
Voer de volgende SQL query uit om je tabel aan te maken, dit zal een voorbeeld zijn dat je gemakkelijk users kunt terug vinden.
Code:
CREATE TABLE users
(
userID int(11) NOT NULL primary key auto_increment,
username varchar(255) NOT NULL DEFAULT ''
);
Ok, database is klaar, voer wat testwaarden in, maakt niet uit wat je als username invoert.
Als je dat gedaan hebt, dan kun je de database wegdoen, hebben we niet meer nodig voorlopig.
HTML formulier aanmaken
Ok, hierin komt wat html met includes naar het javascript bestand en een simpel formulier waarin je de werking van de autocomplete zult zien.
Code:
<html>
<head>
<title>AJAX Tutorial :: AutoComplete</title>
<script src="class_autocomplete.js" type="text/javascript" language="JavaScript"></script>
<style type="text/css">
body
{
font-family: verdana;
font-size: 11px;
}
.namefiller_table
{
width: 125px;
border: 1px solid black;
border-collapse: collapse;
}
.cautocomplete_hilite
{
background-color: #F1F9FC;
color: #C81616;
width: 125px;
font-weight: bold;
font-size: 12px;
vertical-align:middle;
}
.cautocomplete_option
{
font-weight: bold;
font-size: 12px;
background-color:#ffffff;
vertical-align:middle;
width:125px;
color:#39B4E0;
}
.cautocomplete_container
{
margin-left: 5px;
margin-top: 10px;
z-index: 150;
position: absolute;
}
</style>
</head>
<body>
Gezochte gebruikersnaam: <input type="text" autocomplete="off" id="username" value="" />
<div class="autocomplete_container" id="autocomplete_results"></div> <!-- Div waarin resultaten komen -->
<script type="text/javascript" language="JavaScript">
//
// Registreer de autocompleter in de array als een class object
//
register_autocompleter( "ra_tutorial", "autocomplete_results", "username", true ); // ra = registerautocompleter
//
// De properties van de autofiller zijn zo aanroepbaar: AutoCompleters['ra_tutorial'].*
// Dit is niet nodig nu, alles staat wel goed ingesteld
</script>
</body>
</html>
Sla deze op als index.htm.
Wat hebben we gedaan
We hebben een database aangemaakt, en de layout van ons formulier gemaakt en alvast de stijl elementen van de dropdown gedefineerd in het css gedeelte van ons htm bestand.
Nou dit was makkelijk he!
De class schrijven en uitleg
Nu moet er nog wel wat gaan gebeuren in het JS bestand, ik ga alles op volgorde van boven naar beneden laten zien waar welke functie voor is, onderaan vind je een werkend voorbeeld waar alles in zijn geheel staat.
Functie: register_autocompleter
Deze functie houd een array met alle objecten die zijn gemaakt voor het auto completen van velden, waar word dit nou gedaan ? Stel je wilt alle menus sluiten, dan loop je door die array, en roep je de functie in die class aan die het menu dicht doet( display: none; ).
Code:
//
// Array met objecten van de class cAutoComplete
//
var AutoCompleters = new Array();
/**
* Functie die de nieuwe autocompleter toevoegt aan de array van autocompleters, word een object/class
*
* @param control_id string Naam van hoe dit object in de array moet heten
* @param container_id string Div waarin de resultaten komen
* @param txt_id string ID van het tekstveld waarin getypt/gezocht worden
* @param multiple_names bool Sta meerdere resultaten tegelijk toe of niet
*
* @access public
* @return void
*/
function register_autocompleter( control_id, container_id, txt_id, multiple_names )
{
//
// Controleer ofals dit control_id al bestaat, zoja destroy het en maak het opnieuw aan
//
if( AutoCompleters[control_id] )
{
AutoCompleters[control_id] = null;
} // end if
//
// Maak het object daadwerkelijk aan
//
AutoCompleters[control_id] = new cAutoComplete( control_id, container_id, txt_id, multiple_names );
} // end function
De parameters in die functie zijn nodig om weer door te geven aan de werkelijke class.
Deze functie: register_autocompleter staat los van de class, dus hoort BOVEN de class te staan!
Nou de class en zijn private variablen:
Code:
/**
* Class die de AJAX handelingen doet en controleert op invoer van gebruiker enz
*
* @param control_id string Naam van hoe dit object in de array moet heten
* @param container_id string Div waarin de resultaten komen
* @param txt_id string ID van het tekstveld waarin getypt/gezocht worden
* @param multiple_names bool Sta meerdere resultaten tegelijk toe of niet
*
* @access public
* @return void
*/
function cAutoComplete( control_id, container_id, txt_id, multiple_names )
{
/**
* Variablen beheer binnen de klasse, methods and propertys
*
* @var selected int De positie van het geselecteerde item in het menu( in de array met items eigenlijk )
* @var menu_items Array Array met alle resultaten
* @var multiple_names Bool Mogen meerdere namen of niet ?
* @var txt Object Tekst object
* @var container Object Div waar alle resultaten inkomen
* @var control String String waar de this.name van de klasse object in opgeslagen is
* @var menu_open Bool Is het menu zichtbaar of niet ?
* @var max_names int Maximaal aantal resultaten weer te geven in popup
* @var root String Root naar waar ajax.php staat
* @var me Object Referentie naar de root van de class, zodat je de variablen die hier als this gelden vanuit private functies kunt aanroepen met me. ipv this.
*/
this.selected = 0;
this.menu_items = new Array();
this.multiple_names = false;
this.txt = document.getElementById( txt_id );
this.container = document.getElementById( container_id );
this.control = control_id;
this.menu_open = false;
this.max_names = 5;
this.root = "http://localhost/ajax/autocomplete/";
var me = this;
Nougoed, hier word dus de class gedefineerd en alle variablen die hier in gelden zodat alles netjes kan worden
bijgehouden.
Nu komen de functies, ik ga nog steeds van boven naar onder qua code, dus als je alles goed doet en op de volgorde die ik gebruik
in je bestanden zet dan werkt het in 1x
Functie: this.close_popup
Code:
/**
* Functie die de popup laat verdwijnen
*
*/
this.close_popup = function()
{
me.container.style.display = 'none';
me.txt.focus();
me.menu_open = false;
} // end function close_popup
Functie: this.open_popup
Code:
/**
* Functie die de popup laat verschijnen
*
*/
this.open_popup = function()
{
me.container.style.display = 'block';
me.menu_open = true;
} // end function open_popup
Functie: this.txt.onkeyup
Deze functie is vrij groot, dus deze leg ik in delen uit.
Code:
/**
* Functie die luistert naar de toetsen die worden aangeslagen en opdracht geeft om php te laten zoeken naar namen/suggesties
*
* @param e Object Object dat als property onderandere de HEX waarde van de key heeft
*
*/
this.txt.onkeyup = function(e)
{
if( this.value == "" )
{
me.close_popup();
} else { // tekstveld is niet leeg!
// verkrijg de ingedrukte keycode in integer formaat
e = e ? e : window.event;
keycode = e.keyCode;
De functie onkeyup, dat is een event, word aangemaakt en word aangeroepen vanaf het moment dat er een toets word losgelaten
Hiervoor heb je geen extra EventListner troep nodig, dit gaat vanzelf
Het keyword
this is in dit geval het text object, dus niet de class!
Er word eerst gecontroleer ofals de waarde van het tekstveld niet leeg is, zoja popup sluiten anders de keycode opvragen.
In het volgende stukje word gecontroleerd ofals de volgende toetsen worden ingetoetst:
- Enter: Zet het geselecteerde value in de tekstvak
- Escape: focus in tekstveld zetten + popup sluiten
- Pijltje omlaag: 1 item naar beneden verschuiven
- Pijltje omhoog: 1 item naar boven verschuiven
- Andere toetsen: request naar PHP doen en resultaten ontvangen
Dit is wat je gaat zien in het volgende stukje code:
Code:
if( keycode == 13 ) // ## ENTER ##
{
// functie: set_selected() word later behandeld
me.set_selected(); // zet de geselecteerde item waarde in het tekstvak, username is gevonden!
} else if( keycode == 27 ) { // ## ESCAPE ##
// sluit de popup
me.close_popup(); // functie is al behandeld
// zet de focus in het tekstveldje
me.txt.focus(); // standaard functie
} else if( keycode == 38 ) { // ## PIJLTJE OMHOOG ##
if( me.selected <= 0 )
{
me.set_selection( me.menu_items.length-1 ); // functie: set_selection word later behandeld
} else {
me.set_selection(me.selected-1);
}
} else if( keycode == 40 ) { // ## PIJLTJE OMLAAG ##
if( me.selected < me.menu_items.length-1)
{
me.set_selection( me.selected+1 ); // functie: set_selection word later behandeld
} else {
me.set_selection(0);
}
} else { // gewoon letter, vraag suggesties op
Ok dit stukje code lijkt me duidelijk, letter word gecontroleerd, zodra het geen special is dan gewoon op gaan zoeken
Het volgende stukje gaat een request doen naar het PHP bestand, daarna zal het de resultaten ontvangen en verwerken
Code:
//
// Maak het XML object aan, create_sender retourneert een object( XML object )
//
var sender = create_sender(); // functie: create_sender() word later behandeld
Hier is dus het XML object aangemaakt, de functie create_sender() doen we in een extern bestand, maar dat is voor later

Nu ga ik de functie
onreadystatechange tonen, dit is een functie van het XML object
Het is eigenlijk een event die word aangeroepen zodra er data ontvangen word vanuit PHP/ASP
Code:
/**
* Functie die alles registreerd wat er binnenkomt van de server en maakt hier gelijk html van en toont het
*
* @access private
* @return ~
*/
sender.onreadystatechange = function()
{
// Controleer de readyState dit is 0 t/m 4, 1
/*
Meer informatie over de readyState als je een loading bar gaat maken:
http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp
*/
if( sender.readyState == 4 && sender.status == 200 )
{
// De responseText bevat de ontvangen data
if( sender.responseText == "NO_RESULTS" )
{
// sluit de popup, er zijn toch geen resultaten!
me.close_popup();
} else { // resultaten! Splitten en in tabel bouwen.. we ontvangen dit als volgt: Result1;Result2;Result3
//
// Reset de popup en lijsten
//
me.empty_popup();
//
// Vars setup
//
this.received = sender.responseText;
this.splitted = this.received.split( ";" ); // maak een array van de results
// maak alvast een tabel element aan waar alles in komt
var table = document.createElement( "table" );
table.cellPadding = 4;
table.cellSpacing = 1;
table.border = 1;
table.className = "namefiller_table"; // deze class staat in index.htm
// loop alle resultaten af
for( var i =0; i<this.splitted.length-1;i++ )
{
// update de array met huidige menu items
me.menu_items[i] = this.splitted[i];
// maak een nieuwe rij + kolom om de naam in te zetten
var td = table.insertRow(-1).insertCell(-1);
td.className = ( i == me.selected ) ? 'cautofiller_hilite' : 'cautofiller_option';
td.innerHTML = this.splitted[[b][/b]i];
// ken wat info toe aan de td element, dit is belangrijk voor later in de prototype functies om te herkennen welk item we mee werken
td.control = me.control;
td.option_id = i;
td.id = me.control+"_option_"+i; // eigenlijk wat overbodig maar ook handig om alles in 1 string te hebben
//
// Er word later nog een class aangemaakt waarin prototypes in worden afgehandeld, dit leg ik verderop verder uit
//
// zorg dat er wat gebeurt bij mouseovers enz
//
td.onmouseover = cAutoCompleter_Events.prototype.option_mouseover;
td.onmouseout = cAutoCompleter_Events.prototype.option_mouseout;
td.onclick = cAutoCompleter_Events.prototype.option_onclick;
} // end for i
//
// Menu in het popup zetten
//
me.container.appendChild(table);
// popup tonen
me.open_popup();
} // end if sender.responseText no_results check
} // end if readyState en status check
} // end function OntvangSuggesties
//
// verstuur request van suggesties
//
sender.open( "GET", me.root+"ajax.php?action=autocomplete&max_names="+me.max_names+"&value="+this.value, true );
sender.send( "" ); // dit is nu leeg, maak je gebruik van POST methode dan komen hier de variablen, maar let wel op: je moet eerst de headers zetten voordat je POST gebruikt, maar is niet van belang in deze tutorial!
} // end if keycode check
} // end if txt.value == ""
} // end function txt.onkeyup
Tfoe wat een functie! Deze functie doet de volgende acties:
- Controleer invoer van gebruiker
- Indien special var( Enter, Escape, Pijltje down, Pijltje Up ) functie daarvan uitvoeren
- Indien [a-z][A-Z][0-9] word een zoekactie uitgevoerd
- Ontvangen resultaten verwerken in een html tabel
Nu hebben we dus een functie die de popup vult, maar moeten er ook een hebben die de popup weer leegt..
Hier komt die:
Functie: this.empty_popup
Code:
/**
* Functie die alle waarden en alles reset
*
* @access private
* @return ~
*/
this.empty_popup = function()
{
me.selected = 0; // zet selectie weer op 0
me.menu_items = new Array(); // reset de array
// verwijder alle elementen uit de tabel
while( me.container.firstChild )
{
me.container.removeChild( me.container.firstChild );
} // end while
} // end function empty_popup()
Nougoed, nu een wat belangrijkere functie althans, voor de functionaliteit zal deze belangrijk zijn.
Deze functie laat de selectie verspringen als er bijv met een pijltje doorheen gelopen word.
Functie: this.set_selection
Code:
/**
* Functie die de selectie verplaatst
*
* @param selected int Het nieuwe indexID dat geselecteerd eruit moet gaan zien
*
* @access private
* @return ~
*/
this.set_selection = function(selected)
{
//
// Object verkrijgen van de huidig geselecteerde item, zodat deze weer op normaal kan worden gezet
//
var tds = document.getElementById( me.control+"_option_"+me.selected);
if( tds )
{
tds.className = "cautofiller_option"; // verander css class
}
if( selected >= 0 )
{
// update huidig geselecteerde item Index
me.selected = selected;
//
// Verkrijg object van het nieuwe item
//
var tds = document.getElementById( me.control+"_option_"+me.selected);
if( tds )
{
tds.className = "cautofiller_hilite"; // verander css class
}
} // end if
}// end function set_selection
Mooi, nu kunnen we de kleurtjes veranderen, want dat is eigenlijk wat er gebeurd zodat het een menu lijkt

Nu moeten we nog de gevonden waarde in de textbox zetten, dat gaan we in onderstaande code doen:
Functie: this.set_selected
Code:
/**
* Functie die het geselecteerde optie waarde in het tekstveld zet
*
* @access private
* @return ~
*/
this.set_selected = function()
{
if( me.menu_items[me.selected] )
{
// als meerdere resultaten tegelijk mogen, denk aan een mail verzenden aan meerdere leden
if( me.multiple_names == true )
{
me.txt.value = ( me.txt.value == "" ) ? me.menu_items[me.selected] : me.txt.value + ";" + me.menu_items[me.selected];
// verschuif naar volgende item in lijst als dit niet laatste is
if( me.menu_items.length > me.selected+1 )
{
me.set_selection(me.selected+1); // verander selectie
} else {
// einde van de lijst, popup menu op 1 zetten
me.set_selection(0);
} // end if
} else { // gewoon in het tekstveldje zetten, er is maar 1 resultaat tegelijk mogelijk
me.txt.value = me.menu_items[me.selected];
// popup menu meteen laten verdwijnen, mag toch maar 1 nam tegelijk
me.close_popup();
} // end if
} // end if het menu item bestaat niet
} // end function set_selected();
Mooi! En nu ???
Yeah xD
Code:
} // end class cAutoCompleter
Einde van de class!! Het meeste werk is gedaan, we zitten nu op 80% van het werk

Weet je nog dat ik het over prototypes had ? Die gaan ervoor zorgen dat er iets gebeurd als er een
mouseover plaatsvind op een van de items, of een mouseclick enz enz
Deze class is vrij makkelijk te begrijpen daarom doe ik deze in 1x:
Code:
function cAutoCompleter_Events()
{
}
/*
Waar je aan moet denken is dat, het keyword: this het object bevat waarop dit prototype is op toegepast!
Dus pas je een prototype toe op een td, dan is this een CellElement
*/
cAutoCompleter_Events.prototype.option_mouseover = function(e)
{
AutoCompleters[this.control].set_selection(this.option_id);
this.style.cursor = ( !document.all ) ? "pointer" : "hand";
}
cAutoCompleter_Events.prototype.option_onclick = function(e)
{
AutoCompleters[this.control].set_selected(); // met je muis over een item gaan is zelfde als met pijltjes iets selectie effect geven
}
Nog 3 dingen te doen:
- Functie: create_sender(); maken
- ajax.php bouwen
- Testen
Voor de functie create_sender() is het netjes om in een extern bestandje ajax.js oid te stoppen, dit is een functie
die voor andere AJAX handelingen ook prima gebruikt kan worden, het creeërt enkel het XML object.
ajax.js
Functie: create_sender
Code:
/**
* Functie die het XML Sender Object aanmaakt die kan communiceren met PHP/ASP
*
* @access public
* @return XML Object
*/
function create_sender()
{
var objXmlHttp=null;
// reset the object
//xml_obj = null;
// Controleer de browser, IE heeft een ActiveX object, DOM/FF heeft een XML object nodig
if (navigator.userAgent.indexOf("Opera")>=0)
{
alert("Opera ondersteunt geen AJAX, helaas is deze site niet beschikbaar")
return
}
// Internet Explorer
if (navigator.userAgent.indexOf("MSIE")>=0)
{
var strName="Msxml2.XMLHTTP"
if (navigator.appVersion.indexOf("MSIE 5.5")>=0)
{
strName="Microsoft.XMLHTTP"
}
try
{
objXmlHttp=new ActiveXObject(strName)
return objXmlHttp
}
catch(e)
{
alert("Error. Scripting for ActiveX might be disabled");
return
}
}
// FireFox
if (navigator.userAgent.indexOf("Mozilla")>=0)
{
objXmlHttp=new XMLHttpRequest();
return objXmlHttp
}
} // end function create_sender
Staat aardig veel comment in, moet te doen zijn tog

Nu het laatste nog, het ajax.php bestand.
Eerst eens een DB verbinding maken.
ajax.php
Code:
<?
if( !mysql_connect( "localhost", "root", "" ) )
{
//
// Kan niet verbinden, mysql wel aanstaan ?
//
die;
}
if( !mysql_select_db( "autocomplete" ) )
{
//
// Kan niet database selecteren, bestaat wss niet..
//
die;
}
//
// Actie verkrijgen
//
$action = ( isset( $_GET['action'] ) ) ? $_GET['action'] : '';
if( $action == "autocomplete" )
{
//
// Verkrijg waarde + limit
//
$value = ( isset( $_GET['value'] ) ) ? addslashes( $_GET['value'] ) : '';
$limit = ( isset( $_GET['max_names'] ) && is_numeric( $_GET['max_names'] ) && $_GET['max_names'] > 0 ) ? $_GET['max_names'] : 5;
// zoek in de DB als de zoekstring niet leeg is iig
if( $value != '' )
{
//
// Query uitvoeren
//
$sql = mysql_query( "SELECT username FROM users WHERE username LIKE '" . trim($value) . "%' LIMIT 0," . $limit . "");
// hoeveel resultaten zijn er
if( mysql_num_rows( $sql ) > 0 )
{
$return = '';
while( $name = mysql_fetch_array( $sql ) )
{
$return .= htmlentities( $name['username'], ENT_QUOTES ) . ";";
} // end while name
//
// Waarden retourneren in formaat *;*;*;*;
//
echo $return;
} else { // geen resultaten
echo "NO_RESULTS"; // verbergt popup
} // end if check for results
} else { // er is niks in getypt
echo "NO_RESULTS"; // verbergt popup
} // end if value check
}
?>
Hier ga ik geen uitleg over geven, aangezien dit normaal PHP is, en dit een tutorial is over een techniek mbt AJAX.
Nougoed, nu heb ik je alle code gegeven die er moest zijn, ik heb de source gerarred dus veel plezier dermee
Gr,
Chris!