Suchfunktion mit Ajax
Autovervollständigung

DeveloperIT-ProjekteNetzwerk-ManagementNetzwerkeSoftware

Google Suggest ist eine der ersten Ajax-Anwendungen und heute sicherlich die bekannteste. Dieser Artikel zeigt, wie Sie Ihr eigenes Suggest realisieren.

Google-Nachbau

Suchfunktion mit Ajax

Lange bevor der Begriff Ajax sich zum Modewort entwickelt hat, begann Google mit seinen Beta-Anwendungen auf Basis des XMLHttpRequest-Objekts. Einer der Vorreiter ist Google Suggest (www.google.com/ webhp?complete=1&hl=en). Auf den ersten Blick sieht Suggest aus wie die normale Google-Suche. Dieser Eindruck ändert sich, wenn Sie anfangen, ein Suchwort in das Suchfeld einzutippen. Wie von Geisterhand erscheinen Vorschläge für die Suchbegriffe, und Google versieht sie noch mit der Menge an Resultaten für den jeweiligen Begriff. Sie können nun einen dieser Suchbegriffe auswählen oder einfach weitertippen.

Diese Anwendung soll nun im Rahmen dieses Artikels zumindest in weiten Teilen nachgebaut werden. Ähnliche Versuche gibt es bereits.

Die bekannteste Analyse von Google Suggest stammt von Chris Justus (serversideguy.blogspot.com/2004/12/ google-suggest-dissected.html). Einen Suggest-Klon mit PHP versucht Robert Plank (www.developertutorials.com/tutorials/php/google-suggest-with-php-050525/page1.html). Um den Lerneffekt zu erhalten, verzichten die Autoren hier auf jegliche Code-Basis. Gewisse Ähnlichkeiten vor allem in der Ajax-Syntax sind natürlich trotzdem nicht zu vermeiden.


Serverseitig

Suchfunktion mit Ajax

Diese Anwendung kommt ohne Ajax-Bibliothek aus. Gerade für PHP gibt es eine Unmenge an vorgefertigten Ajax-Lösungen, die den Javascript-Code teilweise automatisch produzieren (siehe PHP Professionell, Ausgabe 1/2006, Seite 78). In diesem Fall wird darauf verzichtet, und Sie schreiben den Javascript-Code von Hand. So bleibt das Skript unabhängig von der verwendeten serverseitigen Technik. Das serverseitige Skript entsteht in PHP. Mit jeder beliebigen anderen Technik wäre es aber auch nicht aufwendiger.

Das Skript heißt suggest.php. Sie sollten es auf Ihrem Webserver ablegen. Die Grundlage sind zuerst einmal die Suchbegriffe. In der Praxis stammen sie meist aus einer Datenbank oder einer Datei. Im Beispiel begnügen sich die Autoren mit einer kleinen Auswahl in einem Array:


$schlagwoerter = array('Google', 'Suggest', 'Gospel', 'Suche', 'Grazie', 'Geranie', 'Solist', 'Sopran');

Sie sehen schon: Um den Effekt zu testen, enthält das Array Begriffe, die mit G oder S beginnen. Je größer und besser die Datenquelle, desto hilfreicher werden natürlich die Vorschläge.

Nun müssen die Begriffe gefiltert werden. Das Ergebnis landet in einem Array. Der Suchbegriff aus der Ajax-Anwendung wird als URL-Parameter per GET übermittelt und im Skript ausgelesen:


$ergebnisse = array();
$suchbegriff = isset($_GET['q']) ? $_GET['q'] : '';

Ist der URL-Parameter nicht leer, wird das Array mit den Begriffen durchlaufen und dort der Suchbegriff gesucht. Gibt es eine Übereinstimmung, landet der Begriff im Array für die Ergebnisse:


if ($suchbegriff != '') {
foreach ($schlagwoerter as $schlagwort) {
$teilstring = substr($schlagwort, 0, strlen($suchbegriff));
if ($teilstring == $suchbegriff) {
$ergebnisse[] = $schlagwort;
}
}
}


Ergebnisse sortieren

Suchfunktion mit Ajax

Im Gegensatz zu Google wählen Sie im Beispiel eine alphabetische Sortierung mit sort(). Diese PHP-Funktion sortiert immer das Original-Array.

Anschließend wird das Array in einen Strichpunkt-separierten String umgewandelt und ausgegeben:


sort($ergebnisse);
echo implode($ergebnisse, ';');
exit();
?>

An dieser Stelle ist bereits die Entscheidung für das Ajax-Austauschformat gefallen. Die Anwendung verwendet einen einfachen String mit einem selbst definierten Format zur Trennung der Einzelteile. Ajax bietet zwei Alternativen: die Übermittlung eines XML-Dokuments und die Verwendung des Json-Formats für Javascript-Objekte. XML bietet die bekannten Vor- und Nachteile. Es ist auf der einen Seite strukturiert, flexibel und weiterverwendbar, auf der anderen bedeuten die XML-Tags Daten-Overhead, und auf Javascript-Seite muss fleißig im DOM-Baum gehangelt werden, um vernünftige Ergebnisse zu erhalten. Gerade Letzteres würde den Code dieses Beispiels unnötig aufblähen.

Json ist dagegen ein Format, um Javascript-Objekte als String zu übermitteln (www.json.org). Per Ajax übertragen wird normaler Text. Mit eval() lässt sich der String dann zurückverwandeln. Dieses Format wäre eine gute Alternative zum normalen String.


Ereignis: Texteingabe

Suchfunktion mit Ajax

Nun zur Client-Seite: Sie besteht nur aus einer HTML-Datei suggest.html. Selbstverständlich können Sie die Javascripts auch in eine externe Datei packen. Am Anfang benötigen Sie zuerst das Suchformular. Es besteht aus einem Textfeld und einer Schaltfläche zum Absenden. Außerdem ist ein div-Element eingebaut. Es dient dazu, später die vorgeschlagenen Begriffe anzuzeigen:

   

Die Datei für die Suche (suche.php) existiert in der Anwendung nicht. Sie enthielte normalerweise die eigentliche Suchfunktionalität, wird aber für das hier gezeigte Ajax-Beispiel weggelassen.

Im Beispiel passiert immer dann etwas, wenn der Nutzer einen Buchstaben in das Textfeld eingibt. Deswegen wird beim Ereignis onkeyup die Funktion suggest() aufgerufen und der Wert des Textfelds übermittelt. Selbige Funktion wird außerdem beim Laden der Seite aufgerufen:


Ajax kommt ins Spiel

Suchfunktion mit Ajax

Die Funktion suggest() enthält die Ajax-Logik. Die Variable xmlHttp nimmt das XMLHttpRequest-Objekt auf. wert_global dient dazu, den aktuellen Wert des Textfelds zwischenzuspeichern.


var xmlHttp = null;
var wert_global = "";
function suggest(wert) {
wert_global = wert;
document.formular.suchfeld.focus();

Nun geht es an das XMLHttpRequest-Objekt. Leider gibt es hier Browser-Unterschiede. Der Internet Explorer implementiert es als ActiveX-Objekt, alle anderen Browser als normales Objekt. Dementsprechend hilft eine Fallunterscheidung, die auf die jeweiligen Browserobjekte prüft:


if (window.ActiveXObject) {
try {
xmlHttp= new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp= new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
}
}
} else if (window.XMLHttpRequest) {
try {
xmlHttp= new XMLHttpRequest();
} catch (e) {
}
}

Diese Zeilen können Sie für jede Art von Ajax-Anwendung verwenden.


Erstens: die Ajax-Abfrage

Suchfunktion mit Ajax

Im nächsten Schritt folgt die Ajax-Abfrage. Sie prüfen, ob das XMLHttpRequest-Objekt existiert und beginnen dann mit open() eine Anfrage.


if (xmlHttp) {
xmlHttp.open('GET', 'http://localhost/suggest/suggest_ohneerweiterung.php?q=' + wert, true);
xmlHttp.onreadystatechange = daten;
xmlHttp.send(null);
}
}

Der Suchparameter wird per GET über den URL übergeben. Hier wird der Wert aus dem Textfeld übermittelt, der als Parameter für die Funktion suggest() übergeben wurde. Den URL müssen Sie je nach Adresse Ihres Webservers anpassen. Der dritte Parameter von open() gibt an, dass es sich um eine asynchrone Anfrage handelt. Bei onreadystatechange wird eine Funktion als Event-Handler definiert. Ändert sich also der Status der Ajax-Anfrage, wird diese Funktion aufgerufen. Mit send() wird dann die Anfrage abgeschickt.


Zweitens: der Event-Handler

Suchfunktion mit Ajax

Ein Ajax-Aufruf besteht aus zwei wichtigen Teilen. Den ersten Teil, die Anfrage, haben Sie gerade kennen gelernt. Der zweite Teil ist die Antwort, die im Javascript aufgegriffen werden muss. Dafür ist der Event-Handler zuständig.

Die Daten werden als Strichpunkt-separierte Werte vom serverseitigen Skript übernommen und dann mit der Javascript-Funktion split() in ein Array mit den einzelnen Begriffen zerlegt. Das zugehörige Array wird schon als globale Variable angelegt.


var text = '';
var textteile = new Array();
function daten() {
var ausgabe = '';

Das XMLHttpRequest-Objekt kennt verschiedene Stadien, für die der Event-Handler aufgerufen wird. Erst wenn die Ajax-Abfrage vollständig ist, soll die Antwort ausgelesen werden. Dies entspricht dem Wert 4 für die Eigenschaft readyState.


if (xmlHttp.readyState == 4) {
text = xmlHttp.responseText;

Sollten Sie auf XML als Datenaustauschformat setzen, verwenden Sie statt responseText einfach responseXML. Als Nächstes prüfen Sie, ob der zurückgelieferte String Werte enthält. Wenn ja, wird er in ein Array zerlegt:


if (text != "") {
textteile = text.split(";");

Das Array wird in einer Schleife durchlaufen. Jeder gefundene Begriff landet in der Ausgabe:


for (var teil in textteile) {
ausgabe += "";
ausgabe += textteile[teil] + "
";
}

Jeder Begriff in der Ausgabe besteht aus einem Link. Der Link ruft bei Klick die Funktion wert() auf, die den Begriff als Wert des Textfelds setzt. Im onmouseover-Ereignis wird wert_mark() aufgerufen. Diese Funktion vervollständigt beim Überfahren der vorgeschlagenen Begriffe das Textfeld.

Zum Schluss wird die Variable ausgabe mit den Begriffen in dem div-Element ausgegeben und das div-Element selbst per CSS-Befehl sichtbar gemacht:


document.getElementById('ausgabe').innerHTML = ausgabe;
}
if (ausgabe != "") {
document.getElementById('ausgabe').style.visibility = "visible";
} else {
document.getElementById('ausgabe').style.visibility = "hidden";
}
}
}

Alternativ zur Ausgabe als String könnten Sie natürlich auch per DOM die jeweiligen HTML-Elemente aufbauen.


Ausfüllen

Suchfunktion mit Ajax

Zu guter Letzt zur Funktion wert_mark(): Sie dient dazu, das Feld beim onmouseover vorauszufüllen und den Textbereich zu markieren, der noch nicht vom Nutzer eingetippt wurde. Der Vorteil für den Nutzer liegt auf der Hand: Er kann jederzeit weitere Buchstaben tippen, und der markierte Bereich verschwindet einfach.

Nach einer Überprüfung, ob das Array mit den Begriffen überhaupt gesetzt und der aktuelle Teil vorhanden ist, wird zuerst das Textfeld in eine Variablen gespeichert:


function wert_mark(teil) {
if (textteile[teil] != null && textteile[teil] != "") {
var suchfeld = document.formular.suchfeld;

Anschließend benötigen Sie die Länge des Suchbegriffs, den Sie in der Funktion suggest() in die globale Variable wert_global gesichert haben. Dieser Wert ist gleichzeitig der Startpunkt im Textfeld, ab dem markiert werden soll.

var start = wert_global.length;

Die nächste Variable speichert die Länge des Suchbegriffs:

var laenge = textteile[teil].length;

Anschließend wird im Suchfeld der aktuell mit der Maus überfahrene Begriff eingefügt:

suchfeld.value = textteile[teil];

Nun muss der Bereich, den der Nutzer nicht eingetippt hat, markiert werden. Hier unterscheiden sich Internet Explorer und andere Browser. Der Internet Explorer verwendet createTextRange(). Mit einer Fallunterscheidung wird geprüft, ob das Browserobjekt vorhanden ist:

if (suchfeld.createTextRange) {

Anschließend wird das Objekt erstellt, die Markierung mit moveStart() zum Anfang und mit moveEnd() zum Ende bewegt. Das Ende berechnet sich aus der Länge des Begriffs abzüglich des Starts.


var Auswahl = suchfeld.createTextRange();
Auswahl.moveStart("character", start);
Auswahl.moveEnd("character", laenge - start);
Auswahl.select();

Mozilla, Firefox und Konsorten verwenden setSelectionRange(). Als Parameter erwartet diese Funktion den Startpunkt der Auswahl und die Länge der Auswahl:


} else if (suchfeld.setSelectionRange) {
suchfeld.setSelectionRange(start, laenge);
}

Zuletzt weisen Sie dem Textfeld wieder den Fokus zu.

suchfeld.focus();
}
}


Einschränkung

Suchfunktion mit Ajax

Auf einen komplexen Teil von Google Suggest wurde hier verzichtet: Google Suggest verwendet noch eine Tastatursteuerung, mit der zwischen den Begriffen hin und her geschaltet werden kann. Im Beispiel ist dagegen die Maus allein dazu da, Begriffe auszuwählen und zu bestätigen. Damit wird auch das Google-Suggest-Verhalten verhindert, dass ein Klick auf einen Begriff schon die Suche selbst auslöst.

Wie gewohnt finden Sie die PHP- und die HTML-Datei zum Workshop auf der Heft-CD im Bereich Listings und unter listings.internet-pro.de.

Fazit

Schwer ist die Arbeit mit Ajax nicht. Allerdings sollten Sie auf jeden Fall viel Augenmerk auf das Design und die Usability der Anwendung werfen. Denn schließlich setzt Ajax beim Nutzer Javascript voraus. Und auch eine komplexe Anwendung mit vielen Ereignissen sollte für unerfahrene Nutzer bedienbar bleiben.