Photoshop
Cinema 4d
Fotografie
Weitere Grafiksoftware
HTML / CSS
JavaScript
Flash
PHP
Webserver
Sonstige
Suchfeld mit AJAX - mit und ohne jQuery (JavaScript Tutorial)
Tutorial erstellt von Ingo, letzte Änderung am 16.05.2010Ein AJAX-Einstieg - mit und ohne jQuery
In diesem Tutorial werden Sie erfahren
- wozu AJAX gut ist
- wie man es einsetzt, um ein dynamisches Suchfeld zu realisieren
- wie das JS-Framework jQuery hilft, sich auf das Wesentliche zu konzentrieren
AJAX dient dazu, im Rahmen eines JavaScripts HTTP-Requests an einen Server zu richten und dessen Antwort z.B. in die geladene und vom Nutzer gerade betrachtete Seite einzufügen, ohne dazu diese Seite insgesamt neu laden zu müssen. Wenn Ihnen das völlig klar ist, springen Sie ruhig zum Praxis-Beispiel.
Ansonsten folgen Sie mir auf einem bewußt kurz gehaltenen Rundgang hinter die Kulissen des WWW. Vorweg aber noch ...
nomen est omen?
A = Asynchronous
J = JavaScript
A = And
X = XML
"JavaScript", ok, das dachten Sie sich sicher schon. "And", versuchen Sie mal, AJAX ohne dieses "A" auszusprechen. Bleiben noch "asynchron", wozu wir noch kommen, und XML, was wir vorerst vertagen.
Hinter den Kulissen
Daten-Pingpong im Verborgenen: Das HTTP-request-response-Schema
Wenn der Nutzer einen Link klickt, richtet der Browser an den HTTP-Server eine Anfrage (request). Der Server sieht auf seiner Festplatte nach, ob er das Dokument hat und ob er es ausliefern darf. Falls ja, sendet er eine Antwort (response) mit dem Status (200) samt Text (ok) und den angeforderten Daten. Kurz:

Beide Seiten verständigen sich mittels HTTP, dem HyperText Transfer Protocol. Dort sind Vokabeln festgelegt wie GET, POST oder auch die Statuscodes 200 (ok), 404 (not found). Letzteres kennen auch technisch uninteressierte Surfer; es ist die Standardantwort auf einen "kaputten" Link. Wichtige HTTP-Methoden sind zum Beispiel
GET Beschaffe die durch den URI beschriebene Information (Datei, PHP-Ausgabe...)
POST Sende Daten an Server.
Der Server antwortet mit einem der Response-Statuscodes. Diese dreistelligen Nummern sind in einem Schema von fünf 100er-Gruppen angeordnet. Die Gruppen und einige der häufiger anzutreffenden Codes:
Code:
1xx "continue", wird gesendet, wenn es "hinter den Kulissen" Abstimmungsbedarf gab
2xx "successful", die Anfrage wurde erfolgreich bearbeitet
200 "OK", Anfrage erfolgreich bearbeitet, ggf. folgen Daten
3xx "redirection", zur Beantwortung der Anfrage muss der Client (Browser) noch irgendetwas tun
304 "not modified", seit der letzten Anfrage wurde das angeforderte Dokument nicht verändert
4xx "client error", der Client hat sich in irgendeiner Form geirrt
400 "bad request", Syntaxfehler in der Anfrage (GÄTT :)
404 "not found", der Server konnte die angeforderte Resource nicht finden
5xx "server error", der Server hat ein Problem und kann die Anfrage nicht beantworten
500 "internal server error", ganz allgemeine Rückmeldung
503 "service unavailable", Server kann wegen Überlastung oder Wartung derzeit nicht antworten
2xx "successful", die Anfrage wurde erfolgreich bearbeitet
200 "OK", Anfrage erfolgreich bearbeitet, ggf. folgen Daten
3xx "redirection", zur Beantwortung der Anfrage muss der Client (Browser) noch irgendetwas tun
304 "not modified", seit der letzten Anfrage wurde das angeforderte Dokument nicht verändert
4xx "client error", der Client hat sich in irgendeiner Form geirrt
400 "bad request", Syntaxfehler in der Anfrage (GÄTT :)
404 "not found", der Server konnte die angeforderte Resource nicht finden
5xx "server error", der Server hat ein Problem und kann die Anfrage nicht beantworten
500 "internal server error", ganz allgemeine Rückmeldung
503 "service unavailable", Server kann wegen Überlastung oder Wartung derzeit nicht antworten
Browser und Server schicken einander noch weitere Informationen zu, besonders in Form von HTTP-Headern, also den Kopfzeilen, die den evtl. folgenden Nutzdaten vorausgehen. Im kurzen Abschnitt "POST statt GET" unten gibt es ein Beispiel für den Einsatz von HTTP-Headerzeilen.
Alles muss 'raus! Oder doch nicht?
Sobald die Daten eingetroffen sind, wird der Browser die bisher angezeigte Seite ganz (!) aus seinem Fenster entfernen und das neu eingetroffene Dokument dort darstellen. Das ist aber oft unpraktisch, es führt zum Flackern einer Seite, wenn diese ständig neugeladen wird, nur weil am Rand in einer Box ein paar Daten aktualisiert werden sollen, z.B. in einer Shoutbox oder einem Newsticker.
Hier bietet AJAX die Lösung: Ein JavaScript veranlasst den Browser, im Hintergrund einen Request auszuführen. Statt nun die empfangenen Daten selbst ins Fenster zu laden, reicht der Browser die Daten an das JavaScript weiter, näher gesagt an eine callback-Funktion. Dort kann das Script zum Beispiel mittels DOM-Methoden die Informationen in die bestehende Seite einfügen - ohne komplettes Neuladen der Seite.
Apropos callback - "A" wie asynchron
"Synchron" bedeutet, dass der Browser nach dem Abschicken des Requests erst einmal die Antwort des Servers abwartet, bevor er das Javascript weiter ausführt. Das klingt logisch, ist aber nicht die einzige Möglichkeit. Im asynchronen Arbeitsmodus teilt das Script dem Browser eine "Rückrufnummer" mit, unter der dieser das Script "zurückruft", sobald er Antwort vom Server hat. Das Script arbeitet unterdessen weiter, kann also zum Beispiel trotz längerer Datenübertragung oder Wartezeit sofort nach Starten des Requests wieder Nutzereingaben verarbeiten etc. Als Rückrufnummer dient eine Referenz auf eine Funktion, also deren Name oder eine anonyme Funktion (siehe Beispielcode); eine solche Funktion nennt man "Callback".
Das XMLHttpRequest-Objekt (XHR)
Um die Requests bewerkstelligen zu können, verfügt JavaScript über ein spezielles Objekt mit dem Namen XMLHttpRequest. Die Implementierung ist noch nicht ganz einheitlich: An sich ist es ein Unterobjekt des window-Objektes, im IE jedoch ein ActiveX-Objekt. Wichtige Eigenschaften:
responseText: Die Nutzdaten, die der Server übermittelt hat
status: der HTTP-Responsestatus (200 für ok usw.), nicht zu verwechseln mit dem
readyState: des XMHttpRequest-Objekts selbst; die Werte 0 .. 4 haben folgende Bedeutungen:
Code:
0 "unsent"
1 "opened"
2 "headers received"
3 "loading"
4 "done"
1 "opened"
2 "headers received"
3 "loading"
4 "done"
Der readyState gibt damit an, in welcher Phase der Request-Ausführung sich das XMLHttpRequest-Objekt befindet. Auch er wird derzeit noch uneinheitlich unterstützt; in der Praxis ist "4" der entscheidende.
Ein Request gilt mithin als erfolgreich beendet, wenn a. der readyState "4" und b. der (HTTP-)Status "200" vorliegen. Ab da kann man mit den vom Server übermittelten Nutzdaten arbeiten.
onreadystatechange: Hier die callback-Funktion angeben, die gerufen wird, sobald der Request Fortschritte gemacht hat. - Wichtige Methoden sind open und send, die eine Verbindung zum Server herstellen und ggf. POST-Daten senden.
Praxis: Ein Suchfeld mit dynamischer Trefferauswahl
Sie werden es sicher schon gesehen haben: Noch während man ein Wort in ein Suchfeld tippt, erscheint eine Box mit zur Eingabe passenden Vorschlägen. Hier bei jedem Tastendruck die gesamte Seite nachzuladen, nur um die Trefferliste zu aktualisieren, wäre absurd. Ein klassisches AJAX-Anwendungsbeispiel. Der Ablauf ist aus HTTP-Sicht etwa folgender:

Im HTML-Teil bauen wir uns eine Combobox, also eine Kombination aus Text-Input und Dropdown. Die CSS-IDs von Input-Feld und Listbox-Div unterscheiden sich von der ID der Combobox durch die Zusätze _input und _listbox. Beachten Sie den Eventhandler onkeyup="getItems(this)", an dieser Stelle beginnt das Script seine Arbeit. - CSS-Details finden Sie im Gesamtcode am Ende des Tutorials.
Code:
HTML:
<div id="query" class="combobox">
<input type="text" id="query_input" size="30" onkeyup="getItems(this)" />
<div id="query_listbox" class="listbox"></div>
</div>
<div id="query" class="combobox">
<input type="text" id="query_input" size="30" onkeyup="getItems(this)" />
<div id="query_listbox" class="listbox"></div>
</div>
Nur mit JS-Bordmitteln
Wichtig für den JS-Code sind die IDs und Verwandtschaftsbeziehungen innerhalb der Combobox: Bezeichnen comboBox, comboInput und listbox die entsprechenden HTML-Element(objekt)e, dann ist z.B. comboInput.parentNode ebenso wie listbox.parentNode gerade die comboBox. Oder: listbox.parentNode.id+'_input' ergibt die ID des comboInputs. Das ist also eine Alternative, um von der Listbox zu deren "sibling" zu gelangen.
Die Nutzdaten auf ihrem Weg
Nach jeder Eingabe des Nutzers sendet das Javascript den comboInput-Value an den Server, der wiederum eine kommaseparierte Liste (CSV, comma seperated values) mit Wörtern zurückschickt. Das bewußt einfach gehaltene PHP-Script dazu gibt's ganz unten. Das Javascript trennt die Liste auf, packt die einzelnen Wörter eine in <ul>-Liste und diese wiederum in das Listbox-Div.
Alors on danse - der Code
Wenn der Nutzer etwas ins Input-Feld eingegeben hat, startet getItems() einen Request, sofern überhaupt etwas im Input-Feld steht. Ansonsten blendet er die Trefferliste aus um sicherzustellen, dass unter einem leeren Input keine Trefferliste mehr angezeigt wird. (Bitte die URL anpassen.)
Code:
function getItems(comboInput)
{
var comboBox = comboInput.parentNode;
if(comboInput.value.length > 0)
doHttpRequest('http://localhost/ajax/r.php', comboInput.value, comboBox);
else
hide(comboBox.id+'_listbox');
}
{
var comboBox = comboInput.parentNode;
if(comboInput.value.length > 0)
doHttpRequest('http://localhost/ajax/r.php', comboInput.value, comboBox);
else
hide(comboBox.id+'_listbox');
}
Die Hauptarbeit geschieht nun in zwei Phasen: Zunächst wird in doHttpRequest() ein HTTP-Request erzeugt und gesendet. Wenn die Antwort vom Server eingetroffen ist, wird sie von populateList() ins Dokument integriert.
Code:
function doHttpRequest(url, data, comboBox)
{
var request = null;
// Das XMLHttpRequest-Objekt erzeugen (else-Zweig für den IE)
if (window.XMLHttpRequest)
request = new XMLHttpRequest();
else if (window.ActiveXObject)
try { request = new ActiveXObject("Msxml2.XMLHTTP"); }
catch (e) { };
if(request)
{
// callback definieren, als Funktionsname oder als anonyme Funktion
// In der Funktion verweist "this" auf das XMLHttpRequest-Objekt
request.onreadystatechange =
function()
{
if(this.readyState == 4) // readyState 4 "Done"; das XMLHttpRequest-Objekt hat seine Arbeit getan
{
if(this.status == 200) // Der Server meldet HTTP-Status 200 "ok"
populateList(comboBox);
}
};
// Request ausfuehren, per GET, dritter Parameter ist true = asynchron (false: synchron)
request.open('GET', url+'?query='+encodeURIComponent(data), true);
request.send();
// Wir merken uns den Request als Unterobjekt, so ist er später leicht
// und eindeutig erreichbar, auch wenn mehrere Requests parallel laufen
comboBox.request = request;
}
};
{
var request = null;
// Das XMLHttpRequest-Objekt erzeugen (else-Zweig für den IE)
if (window.XMLHttpRequest)
request = new XMLHttpRequest();
else if (window.ActiveXObject)
try { request = new ActiveXObject("Msxml2.XMLHTTP"); }
catch (e) { };
if(request)
{
// callback definieren, als Funktionsname oder als anonyme Funktion
// In der Funktion verweist "this" auf das XMLHttpRequest-Objekt
request.onreadystatechange =
function()
{
if(this.readyState == 4) // readyState 4 "Done"; das XMLHttpRequest-Objekt hat seine Arbeit getan
{
if(this.status == 200) // Der Server meldet HTTP-Status 200 "ok"
populateList(comboBox);
}
};
// Request ausfuehren, per GET, dritter Parameter ist true = asynchron (false: synchron)
request.open('GET', url+'?query='+encodeURIComponent(data), true);
request.send();
// Wir merken uns den Request als Unterobjekt, so ist er später leicht
// und eindeutig erreichbar, auch wenn mehrere Requests parallel laufen
comboBox.request = request;
}
};
Nach Eintreffen der Serverantwort füllt populateList() die Trefferliste in der Combobox, falls Treffer vorliegen. Es blendet in jedem Falle zunächst die Listbox aus, um sicherzustellen, dass keine leere Trefferliste angezeigt wird.
Code:
function populateList(comboBox)
{
var listbox = document.getElementById(comboBox.id+'_listbox');
hide(listbox);
// Als responseText erhalten wir eine CSV-Liste
var responseText = comboBox.request.responseText;
// Diese kommaseparierte Liste wandeln wir in eine <ul> und bauen sie ein
if(responseText.length > 0)
{
var items = responseText.split(',');
var xhtml = '<ul>';
for(var k=0; k<items.length; k++)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.innerHTML = xhtml;
show(listbox);
}
};
{
var listbox = document.getElementById(comboBox.id+'_listbox');
hide(listbox);
// Als responseText erhalten wir eine CSV-Liste
var responseText = comboBox.request.responseText;
// Diese kommaseparierte Liste wandeln wir in eine <ul> und bauen sie ein
if(responseText.length > 0)
{
var items = responseText.split(',');
var xhtml = '<ul>';
for(var k=0; k<items.length; k++)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.innerHTML = xhtml;
show(listbox);
}
};
Wenn der Nutzer einen Eintrag in der Trefferliste anklickt, dann wird dieser ins Input-Feld übertragen und die Listbox ausgeblendet.
Code:
function copyToInput(listItem)
{
var listbox = listItem.parentNode.parentNode;
var comboBox = listbox.parentNode;
document.getElementById(comboBox.id+'_input').value =
listItem.textContent || listItem.innerHTML;
hide(listbox);
}
{
var listbox = listItem.parentNode.parentNode;
var comboBox = listbox.parentNode;
document.getElementById(comboBox.id+'_input').value =
listItem.textContent || listItem.innerHTML;
hide(listbox);
}
Die Funktionen hide()und show() sind kleine Utilities, die ein Element zeigen oder verstecken. Als Argument wird das Elementobjekt oder seine ID übergeben.
Den Code dazu finden Sie unten in der Zusammenstellung der Codebeispiele.
POST statt GET
Im Beispiel wurde der Query-String per GET-Methode an den Server geschickt. Bei umfangreicheren Daten möchte man vielleicht die Daten per POST senden. Hierzu informiert man den Server mittels HTTP-Headerzeilen über Art und Länge der übermittelten Daten und sendet diese dann als Argument von send(). Änderungen betreffen den letzten Teil von doHttpRequest():
Code:
var params = 'query='+encodeURIComponent(data);
request.onreadystatechange = function () { /* ... wie oben ... */ }
request.open('POST', url, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);
request.onreadystatechange = function () { /* ... wie oben ... */ }
request.open('POST', url, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);
Frameworks nutzen - Zum Beispiel jQuery
"Write less, do more"
Dies hat sich jedenfalls jQuery auf die Fahnen geschrieben. Tatsächlich können wir dieses Framework, dass wir hier beispielhaft herauspicken, gewinnbringend einsetzen. Einerseits bietet jQuery eine umfangreiche AJAX-Unterstützung, andererseits bietet es eine Reihe bequemer Methoden, um ein HTML-Dokument zu verändern. Zunächst binden wir jQuery selbst ein (Pfad bitte anpassen):
Code:
<script src="js/jquery-1.4.2.min.js" type="text/javascript"></script>
Für AJAX hat jQuery eine gleichnamige Methode samt Optionen zum Anpassen der Methode. Die Details der Request-Erzeugung übernimmt jQuery. Wir müssen nur sagen, woher die Daten kommen sollen und was wir mit ihnen anstellen wollen, wenn sie erst einmal eingetroffen sind. Hierzu geben wir unter "success" ein Callback an. "success" wird gerufen, wenn der Request erfolgreich beendet wurde.
Möchte man auch den Fehlerfall behandeln, so kann man unter "error" ein Callback angeben, um zum Beispiel den Nutzer über die Situation zu informieren.
Zusammen mit den verschiedenen weiteren Möglichkeiten von jQuery, besonders der selektorgesteuerte Elementauswahl und den Methoden hide() und show(), die jQuery ebenfalls für uns bereithält, ergibt sich ein kompaktes, lesbares Script:
Code:
// Gesamt-Javascript
function getItems(comboInput)
{
var listbox = $(comboInput).siblings('.listbox').hide();
if(comboInput.value.length > 0)
{
jQuery.ajax
({
'url' : 'r.php',
'data': 'query='+encodeURIComponent(comboInput.value),
'success': function(msg) { populateList(msg, listbox); }
});
};
}
function populateList(msg, listbox)
{
if(msg.length > 0)
{
var items = msg.split(',');
var xhtml = '<ul>';
for(var k in items)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.html(xhtml).show();
}
}
// Click auf ein Item in der Listbox: Item in das comboInput kopieren
function copyToInput(listItem)
{
$(listItem).parents('.listbox')
.hide()
.siblings('input')
.val($(listItem).text());
}
function getItems(comboInput)
{
var listbox = $(comboInput).siblings('.listbox').hide();
if(comboInput.value.length > 0)
{
jQuery.ajax
({
'url' : 'r.php',
'data': 'query='+encodeURIComponent(comboInput.value),
'success': function(msg) { populateList(msg, listbox); }
});
};
}
function populateList(msg, listbox)
{
if(msg.length > 0)
{
var items = msg.split(',');
var xhtml = '<ul>';
for(var k in items)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.html(xhtml).show();
}
}
// Click auf ein Item in der Listbox: Item in das comboInput kopieren
function copyToInput(listItem)
{
$(listItem).parents('.listbox')
.hide()
.siblings('input')
.val($(listItem).text());
}
Dieses Script nutzt übrigens keinerlei CSS-IDs mehr. Auch darum muss man sich also keine Gedanken mehr machen.
The shortest of them all - jQuery.load()
Oft geht es nur darum, ein HTML-Fragment anzufordern und in ein bestehendes Dokument einzufügen. Für diesen Zweck bietet jQuery eine Kurzform:
Code:
$(elem).load(url)
load() startet den Request an die URL und ersetzt dann automatisch den Inhalt des Elementes durch die Antwort - eine sehr gute Idee!
Wenn unser PHP-Script nicht mehr eine CSV-Liste liefert, sondern eine ausformulierte HTML-<ul>, dann kann das clientseitige Javascript noch weiter schrumpfen:
Code:
// Gesamt-Javascript (nutzt modifiziertes php-Script, + 1 Zeile)
function getItems(comboInput)
{
var listbox = $(comboInput).siblings('.listbox').hide();
if(comboInput.value.length > 0)
listbox.load("r.php?query="+encodeURIComponent(comboInput.value)).show();
}
// Click auf ein Item in der Listbox: Item in das comboInput kopieren
function copyToInput(listItem)
{
$(listItem).parents('.listbox')
.hide()
.siblings('input')
.val($(listItem).text());
}
function getItems(comboInput)
{
var listbox = $(comboInput).siblings('.listbox').hide();
if(comboInput.value.length > 0)
listbox.load("r.php?query="+encodeURIComponent(comboInput.value)).show();
}
// Click auf ein Item in der Listbox: Item in das comboInput kopieren
function copyToInput(listItem)
{
$(listItem).parents('.listbox')
.hide()
.siblings('input')
.val($(listItem).text());
}
Viel kürzer -und dennoch lesbar- geht's dann wahrscheinlich nicht mehr.
Soweit dies.
Vielleicht ist es an dieser Stelle Zeit, selbst mit dem Script zu experimentieren. Oder Sie werfen einen Blick in die sehr gelungene jQuery-Dokumentation. Für alle Fälle und zum Abschluss hier der gesamte Code (HTML, CSS, JS, PHP) in den beschriebenen Varianten ohne und mit jQuery:
Zusammenstellung der Codebeispiele
Es sind folgende Dateien im Spiel:
index.html Enthält den HTML/CSS/JS-Code
r.php Ein kurzes PHP-Script, das zu einem String eine Liste mit Vorschlägen zurückschickt
top1000de-utf8.txt Eine Textdatei mit 1000 Wörtern, pro Zeile 1 Wort (unsere beispiel-Datenbasis)
jquery-1.4.2.min.js jQuery in einer aktuellen Version, evtl. "minifiziert", Link siehe unten, Linkliste
Denken Sie bitte daran, die URL des PHP-Scripts und Pfade zu JS und Textdatei anzupassen. Dies betrifft die Funktion getItems() bzw. doHttpRequest() sowie das PHP-Script.
"Zu Fuß" - ohne jQuery
Code:
// ohne jQuery - die längste Version, komplett mit HTML/CSS
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title></title>
<meta http-equiv="content-type" content="text/html;charset=ISO-8859-1" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<script type="text/javascript">
/* <![CDATA[ */
// Bei Tastendruck im Suchfeld (Input) einen HTTP-Request auslösen
function getItems(comboInput)
{
var comboBox = comboInput.parentNode;
if(comboInput.value.length > 0)
doHttpRequest('http://localhost/ajax/r.php', comboInput.value, comboBox);
else
hide(comboBox.id+'_listbox');
}
// Einen XMLHttpRequest erzeugen und senden
function doHttpRequest(url, data, comboBox)
{
var request = null;
// Das XMLHttpRequest-Objekt erzeugen (else-Zweig für den IE)
if (window.XMLHttpRequest)
request = new XMLHttpRequest();
else if (window.ActiveXObject)
try { request = new ActiveXObject("Msxml2.XMLHTTP"); }
catch (e) { };
if(request)
{
// callback definieren, als Funktionsname oder als anonyme Funktion
// In der Funktion verweist this auf das XMLHttpRequest-Objekt
request.onreadystatechange =
function()
{
if(this.readyState == 4) // readyState 4 "Done"
{
if(this.status == 200) // HTTP-Status 200 "ok"
populateList(comboBox);
}
};
// Request ausfuehren, per GET, dritter Parameter ist true = asynchron (false: synchron)
request.open('GET', url+'?query='+encodeURIComponent(data), true);
request.send();
// Wir merken uns den Request als Unterobjekt, so ist er später leicht
// und eindeutig erreichbar, auch wenn mehrere Requests parallel laufen
comboBox.request = request;
}
};
// Empfangende Daten ins Dokument integrieren
function populateList(comboBox)
{
var listbox = document.getElementById(comboBox.id+'_listbox');
hide(listbox);
// Als responseText erhalten wir eine CSV-Liste und bauen diese zu einer <ul> um
var responseText = comboBox.request.responseText;
if(responseText.length > 0)
{
var items = responseText.split(',');
var xhtml = '<ul>';
for(var k=0; k<items.length; k++)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.innerHTML = xhtml;
show(listbox);
}
};
// Bei Klick auf einen Eintrag in der Listbox diesen Eintrag ins Input kopieren
function copyToInput(listItem)
{
var listbox = listItem.parentNode.parentNode;
var comboBox = listbox.parentNode;
document.getElementById(comboBox.id+'_input').value =
listItem.textContent || listItem.innerHTML;
hide(listbox);
}
// Utilities: Elemente verstecken oder zeigen
function hide(thing) // thing: HTML-Elementobjekt oder ID-String
{
var isObj = (typeof(thing) == 'object');
var el = isObj ? thing : document.getElementById(thing);
if(el) el.style.display = 'none';
}
function show(thing,mode)
{
var isObj = (typeof(thing) == 'object');
var el = isObj ? thing : document.getElementById(thing);
if(el) el.style.display = mode?mode:'block';
}
/* ]]> */
</script>
<style type="text/css">
/* <![CDATA[ */
.combobox { position:relative; float:left; width:300px; border:1px dashed red; }
.combobox input { height:1.3em; }
.combobox .listbox
{
display:none;
position:absolute;
top:1.3em; left:0px;
max-height:250px;
overflow:auto;
border:1px solid #666666;
background:#ffffff;
}
.combobox .listbox ul { margin:0; padding:0; list-style-type:none; }
.combobox .listbox ul li { padding:2px 30px 2px 10px; margin:1px; cursor:pointer; }
.combobox .listbox ul li:hover { background:#eeeeee; }
/* ]]> */
</style>
</head>
<body>
<h3>Dynamische Suchfelder - hier die Variante ohne jQuery</h3>
<div id="query" class="combobox">
<input type="text" id="query_input" size="30" onkeyup="getItems(this)" />
<div id="query_listbox" class="listbox"></div>
</div>
<!-- Testhalber ein zweites Suchfeld im gleichen Dokument -->
<div id="query2" class="combobox">
<input type="text" id="query2_input" size="30" onkeyup="getItems(this)" />
<div id="query2_listbox" class="listbox"></div>
</div>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title></title>
<meta http-equiv="content-type" content="text/html;charset=ISO-8859-1" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<script type="text/javascript">
/* <![CDATA[ */
// Bei Tastendruck im Suchfeld (Input) einen HTTP-Request auslösen
function getItems(comboInput)
{
var comboBox = comboInput.parentNode;
if(comboInput.value.length > 0)
doHttpRequest('http://localhost/ajax/r.php', comboInput.value, comboBox);
else
hide(comboBox.id+'_listbox');
}
// Einen XMLHttpRequest erzeugen und senden
function doHttpRequest(url, data, comboBox)
{
var request = null;
// Das XMLHttpRequest-Objekt erzeugen (else-Zweig für den IE)
if (window.XMLHttpRequest)
request = new XMLHttpRequest();
else if (window.ActiveXObject)
try { request = new ActiveXObject("Msxml2.XMLHTTP"); }
catch (e) { };
if(request)
{
// callback definieren, als Funktionsname oder als anonyme Funktion
// In der Funktion verweist this auf das XMLHttpRequest-Objekt
request.onreadystatechange =
function()
{
if(this.readyState == 4) // readyState 4 "Done"
{
if(this.status == 200) // HTTP-Status 200 "ok"
populateList(comboBox);
}
};
// Request ausfuehren, per GET, dritter Parameter ist true = asynchron (false: synchron)
request.open('GET', url+'?query='+encodeURIComponent(data), true);
request.send();
// Wir merken uns den Request als Unterobjekt, so ist er später leicht
// und eindeutig erreichbar, auch wenn mehrere Requests parallel laufen
comboBox.request = request;
}
};
// Empfangende Daten ins Dokument integrieren
function populateList(comboBox)
{
var listbox = document.getElementById(comboBox.id+'_listbox');
hide(listbox);
// Als responseText erhalten wir eine CSV-Liste und bauen diese zu einer <ul> um
var responseText = comboBox.request.responseText;
if(responseText.length > 0)
{
var items = responseText.split(',');
var xhtml = '<ul>';
for(var k=0; k<items.length; k++)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.innerHTML = xhtml;
show(listbox);
}
};
// Bei Klick auf einen Eintrag in der Listbox diesen Eintrag ins Input kopieren
function copyToInput(listItem)
{
var listbox = listItem.parentNode.parentNode;
var comboBox = listbox.parentNode;
document.getElementById(comboBox.id+'_input').value =
listItem.textContent || listItem.innerHTML;
hide(listbox);
}
// Utilities: Elemente verstecken oder zeigen
function hide(thing) // thing: HTML-Elementobjekt oder ID-String
{
var isObj = (typeof(thing) == 'object');
var el = isObj ? thing : document.getElementById(thing);
if(el) el.style.display = 'none';
}
function show(thing,mode)
{
var isObj = (typeof(thing) == 'object');
var el = isObj ? thing : document.getElementById(thing);
if(el) el.style.display = mode?mode:'block';
}
/* ]]> */
</script>
<style type="text/css">
/* <![CDATA[ */
.combobox { position:relative; float:left; width:300px; border:1px dashed red; }
.combobox input { height:1.3em; }
.combobox .listbox
{
display:none;
position:absolute;
top:1.3em; left:0px;
max-height:250px;
overflow:auto;
border:1px solid #666666;
background:#ffffff;
}
.combobox .listbox ul { margin:0; padding:0; list-style-type:none; }
.combobox .listbox ul li { padding:2px 30px 2px 10px; margin:1px; cursor:pointer; }
.combobox .listbox ul li:hover { background:#eeeeee; }
/* ]]> */
</style>
</head>
<body>
<h3>Dynamische Suchfelder - hier die Variante ohne jQuery</h3>
<div id="query" class="combobox">
<input type="text" id="query_input" size="30" onkeyup="getItems(this)" />
<div id="query_listbox" class="listbox"></div>
</div>
<!-- Testhalber ein zweites Suchfeld im gleichen Dokument -->
<div id="query2" class="combobox">
<input type="text" id="query2_input" size="30" onkeyup="getItems(this)" />
<div id="query2_listbox" class="listbox"></div>
</div>
</body>
</html>
Mit jQuery
Code:
// Der JS-Code mit jQuery. Nur das JS ändert sich, HTML/CSS und PHP bleiben
<script src="js/jquery-1.4.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
/* <![CDATA[ */
// NUR der JS-Teil wurde verändert, HTML/CSS und PHP bleiben
// Bei Tastendruck im Suchfeld (Input) einen HTTP-Request ausführen
function getItems(comboInput)
{
var listbox = $(comboInput).siblings('.listbox').hide();
if(comboInput.value.length > 0)
{
jQuery.ajax
({
'url' : 'r.php',
'data': 'query='+encodeURIComponent(comboInput.value),
'success': function(msg) { populateList(msg, listbox); }
});
};
}
// Empfangende Daten ins Dokument integrieren
function populateList(msg, listbox)
{
if(msg.length > 0)
{
var items = msg.split(',');
var xhtml = '<ul>';
for(var k in items)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.html(xhtml).show();
}
}
// Bei Klick auf einen Eintrag in der Listbox diesen Eintrag ins Input kopieren
function copyToInput(listItem)
{
$(listItem).parents('.listbox')
.hide()
.siblings('input')
.val($(listItem).text());
}
/* ]]> */
</script>
<script src="js/jquery-1.4.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
/* <![CDATA[ */
// NUR der JS-Teil wurde verändert, HTML/CSS und PHP bleiben
// Bei Tastendruck im Suchfeld (Input) einen HTTP-Request ausführen
function getItems(comboInput)
{
var listbox = $(comboInput).siblings('.listbox').hide();
if(comboInput.value.length > 0)
{
jQuery.ajax
({
'url' : 'r.php',
'data': 'query='+encodeURIComponent(comboInput.value),
'success': function(msg) { populateList(msg, listbox); }
});
};
}
// Empfangende Daten ins Dokument integrieren
function populateList(msg, listbox)
{
if(msg.length > 0)
{
var items = msg.split(',');
var xhtml = '<ul>';
for(var k in items)
xhtml += '<li onclick="copyToInput(this)">'+items[k]+'<\/li>';
xhtml += '<\/ul>';
listbox.html(xhtml).show();
}
}
// Bei Klick auf einen Eintrag in der Listbox diesen Eintrag ins Input kopieren
function copyToInput(listItem)
{
$(listItem).parents('.listbox')
.hide()
.siblings('input')
.val($(listItem).text());
}
/* ]]> */
</script>
PHP und Wortliste
Das PHP-Script liest eine Datei mit 1000 Wörtern ein, sucht diejenigen heraus, die wie $_GET['query'] anfangen und fügt sie zu einer kommaseparierten Liste (CSV) zusammen. Diese wird als Antwort an den Browser geschickt. Alternativ wird eine fertige HTML-<ul> erzeugt:
Code:
// Datei r.php
<?php
$words = file('top1000de-utf8.txt'); // Datei mit 1000 Wörtern, 1 Wort pro Zeile
$res = array();
function copywords($item, $key)
{
global $res;
if(stripos($item, $_GET['query']) === 0) // auch Typgleichheit wichtig!
array_push($res, $item);
natcasesort($res);
};
array_walk($words, 'copywords');
$s = implode(',', $res);
/* ------- Variante, die einen HTML-<ul> erzeugt statt einer CSV: ----------
// --- Die jQuery-load()-Variante nutzt diesen Teil statt der Zeile oben ---
$s = implode('</li><li onclick="copyToInput(this)">', $res);
if (count($res)>0) $s = '<ul><li onclick="copyToInput(this)">'.$s.'</li></ul>';
*/
echo $s;
?>
<?php
$words = file('top1000de-utf8.txt'); // Datei mit 1000 Wörtern, 1 Wort pro Zeile
$res = array();
function copywords($item, $key)
{
global $res;
if(stripos($item, $_GET['query']) === 0) // auch Typgleichheit wichtig!
array_push($res, $item);
natcasesort($res);
};
array_walk($words, 'copywords');
$s = implode(',', $res);
/* ------- Variante, die einen HTML-<ul> erzeugt statt einer CSV: ----------
// --- Die jQuery-load()-Variante nutzt diesen Teil statt der Zeile oben ---
$s = implode('</li><li onclick="copyToInput(this)">', $res);
if (count($res)>0) $s = '<ul><li onclick="copyToInput(this)">'.$s.'</li></ul>';
*/
echo $s;
?>
Fehlt noch die "Datenbasis", in diesem Falle die 1000 häufigsten deutschen Wörter. Link siehe Linkliste. Anstelle einer Datei könnte natürlich auch ein mySQL-Request stehen.
Code:
der
die
und
in
den
von
zu
...
Links
W3C - Das XMLHttpRequest-Objekt
jQuery-Dokumentation (und Link zum Download)
HTTP 1.1 Specifications (RFC 2616)
Uni Leipzig: Liste der 1000 häufigsten Wörter
>> Allgemeine Fragen oder Probleme mit dem Tutorial? Hier gehts zum Forum!