Kategorien
Webdesign

AJAX in der Praxis: Ein einfacher Chat

Ein Online-Chat braucht beispielsweise Java. Mit der Integration verschiedener Techniken in AJAX ist es jedoch möglich, einen einfachen Onlinechat auf schnelle Weise in eine gewöhnliche Webseite zu integrieren. Ein schlichter Onlinechat ist eigentlich schwierig…

Ein Online-Chat braucht beispielsweise Java. Mit der Integration verschiedener Techniken in AJAX ist es jedoch möglich, einen einfachen Onlinechat auf schnelle Weise in eine gewöhnliche Webseite zu integrieren.

Ein schlichter Onlinechat ist eigentlich schwierig zu realisieren, da die neu eingegangenen Messages stets vom Server geladen werden müssen. Dies bedeutete bisher, dass die Seite neu geladen werden musste. Um dies zu umgehen, hat man auf andere Möglichkeiten gesetzt, wie zum Beispiel JavaApplets. Mit AJAX wird die Sache erleichtert, da man die Seiten nicht mehr neu im Browser laden muss. Die periodische Überprüfung nach neuen Messages kann über JavaScript und XMLHttpRequests mit der setTimeOut-Funktion gesteuert werden.

Hört sich einfach an – ist es im Grunde auch so. Bei Dr.Web wurde bereits einmal von mir ein Skript vorgestellt, mit dem auf einfache Art eine Shoutbox programmiert wurde. Dieses Skript soll nun Schritt-für-Schritt AJAX tauglich gemacht werden und zu einem Chat umfunktioniert werden.

Im Internet gibt es bereits OpenSource-AJAX-Chat-Anwendungen, zum Beispiel Lace und Treehouse. Unser Beispiel wurde entwickelt, um im Rahmen dieser Artikelserie AJAX näher zu bringen. Das Beispiel weist längst nicht alle Features auf, die möglich wären, ist so aber besser verständlich.

Rollen wir das Feld von hinten auf und beginnen wir mit dem so genannten Server Backend. In unserem Fall ist das eine MySQL Datenbank. Für unsere kleine Anwendung verwenden wir eine Tabelle Namens „ajaxChat“, welche die Felder id, name, nachricht und date hat; diese kann mit folgender SQL-Anweisung erzeugt werden:

  CREATE TABLE `ajaxChat` (
    `id` LONGINT NOT NULL AUTO_INCREMENT ,
    `name` VARCHAR( 60 ) NOT NULL ,
    `nachricht` VARCHAR( 120 ) NOT NULL ,
    `date` VARCHAR( 30 ) NOT NULL ,
    PRIMARY KEY ( `id` )
    );

Screenshot

Zunächst wird das Backend angegangen; zum einen die Datenbank, zum anderen das PHP-Skript, welches die XML-Antworten auf die http-Anfragen erzeugt.

Das PHP-Skript (nennen wir es ajaxchat.php) auf der Serverseite muss folgende Funktionalitäten aufweisen:

  • Verbindung zur Datenbank öffnen und schließen.
  • Nneue Einträge müssen in die Datenbank eingefügt werden können.
  • Neue Einträge müssen abgeholt werden können; als Erkennung dient eine eindeutige ID.

Die Ausgabe der Daten, das heißt die asynchrone Antwort des Servers erfolgt über XML; dieses wird dann über JavaScript und CSS im Browser dargestellt.

Die Datenbankverbindung kann über folgenden Zeilen-Code geöffnet werden; die Variablen müssen an die jeweilige Serverumgebung angepasst werden.

       <?php
    //wichtig; damit nicht gecached wird!
    header( "Cache-Control: no-cache, must-revalidate" );
    header( "Pragma: no-cache" );
    //damit unser JavaScript die Daten auch als XML erkennt
    header("Content-Type: text/xml");

    $db_host = "localhost"; // Host
    $db_user = "root"; // User
    $db_password = ""; // Passwort
    $db_name = "test"; // Datenbank

    mysql_connect($db_host,$db_user,$db_password) or die(mysql_error());
    mysql_select_db($db_name) or die(mysql_error()); 

Nachrichten auslesen
Die Funktion, mit der stets die aktuellsten Nachrichten aus der Datenbank extrahiert werden:

      function getLatestEntries($latestID) {

    $query = "SELECT id, name, nachricht, date FROM ajaxchat WHERE id > $latestID
    ORDER BY id DESC LIMIT 20";
    $erg = mysql_query($query);
    echo "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
    echo "<messages>";
    while($erg2=mysql_fetch_array($erg)) //Erzeugung der XML Ausgabe
    {
    echo "<message><id>".$erg2['id'];
    echo "</id><name>".$erg2['name'];
    echo "</name><nachricht>".$erg2['nachricht'];
    //Formatierung des Timestamps
    echo "</nachricht><date>".date("d.m.Y H:i",$erg2['date']);
    echo "</date></message>";
    }
    echo "</messages>";
    }

Bei dieser Funktion kommt es insbesondere auf die SQL-Abfrage an; über die Bedingung id > $latestID, werden lediglich die Einträge ermittelt, deren ID größer (d.h. deren Einträge „frischer“ sind), ermittelt. Die Variable latestID wird, wie wir weiter unten sehen werden, bei dem http Requests über JavaScript übergeben.

Durch den Zusatz ORDER BY id DESC in der SQL Abfrage wird sichergestellt, dass die Einträge absteigend ausgegeben werden; LIMIT 20 beschränkt die Ausgabe auf die letzten zwanzig Einträge. Die Erzeugung des XML-Codes erfolgt schlicht über die echo-Funktion (eine Verwendung der DOMXML Funktionen von PHP wäre bei dem geringen Aufwand nicht gerechtfertigt).

Neue Einträge
Das erzeugen von neuen Einträgen in die Tabelle erfolgt über folgende Funktion:

      function createNewEntry($name, $nachricht) {

    //HTML Tags entfernen
    $name = strip_tags($name, '');
    $nachricht = strip_tags($nachricht,''); 

    $query = "INSERT INTO ajaxchat(name, nachricht, date)
    VALUES ('$name','$nachricht','".time()."')";
    echo "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
    if (!mysql_query($query)) {
    //nicht notwendig, dient nur der Fehlerkontrolle
    echo "<createNewEntry>0</createNewEntry>";}
    else {
    echo "<createNewEntry>1</createNewEntry>";
    }
    }

Dabei erzeugt die Funktion createNewEntry wiederum eine XML-Antwort; dieser schenken wir aber keine Beachtung. Sie wurde lediglich zur Fehlerüberprüfung implementiert, so dass man über JavaScript, falls es einen MySQL-Fehler beim Erzeugen eines neuen Eintrag gäbe, dies entsprechend im Browser anzeigen könnte – wenn man denn wollte.

Skriptsteuerung
Da alle Anfragen an ein und dasselbe Skript geschickt werden (ajaxchat.php), benötigen wir noch ein paar Zeilen-Code, mit denen sichergestellt wird, dass bei den Aufrufen auch die jeweils angesprochene Funktion ausgeführt wird. Wir arbeiten hierbei mit Parametern, die in die http Anfragen über das XMLHttpRequest Objekt eingebaut sind.

      //falls action=createNewEntry, Erzeugung eines neuen Eintrages
    if ($_GET['action'] == "createNewEntry") {
    createNewEntry($_GET['name'], $_GET['nachricht']);
    }
    //falls action=getLatestEntries, Ausgabe der neuesten Einträge
    elseif ($_GET['action'] == "getLatestEntries") {
    getLatestEntries($_GET['latestID']);
    }
    mysql_close(); //schließen der PHP Verbindung
    ?>

Die AJAX Engine

Screenshot

Im zweiten Schritt wird die AJAX-Engine aufgebaut; mit ihr werden die http-Anfragen erzeugt und die Ergebnisse im Browser dargestellt

Die AJAX-Engine hat folgende Aufgaben:

  • Erzeugung der http-Anfragen um neue Nachrichten vom Server zu erfragen-
  • Erzeugung der http-Anfragen, über die neue Einträge in der Datenbank gespeichert werden.

Da später das Empfangen von Daten automatisiert wird beziehungsweise über die setTimeOut-Funktion gesteuert wird, ist es notwendig, dass wir für unsere Anwendung zwei XMLHttpRequest-Objekte erzeugen. Damit wird sichergestellt, dass man gleichzeitig sowohl Daten empfangen als auch senden kann.

Erzeugung zweier XMLHttpRequest-Objekte
Das JavaScript zur Erzeugung der XMLHttpRequest-Objekte ist identisch mit dem aus Teil 2 dieser Artikelserie:

       <script type="text/javascript">
    var latestID = 0; //latestID als globale Variable; wichtig!

    function createXMLHttpReqObj() { //erzeugt die XMLHttpRequest Objekte

    // für Mozilla etc.
    if(window.XMLHttpRequest) {
    try { //Fehler abfangen
    req = new XMLHttpRequest();
    } catch(e) {
    req = false;
    }
    // für den InternetExplorer
    } else if(window.ActiveXObject) {
    try {
    req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
    try {
    req = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
    req = false;
    }
    }
    }
    return req;
    }
    // Initialisierung der beiden Objekte
    var httpGetLatestEntries= createXMLHttpReqObj();
    var httpCreateNewEntry = createXMLHttpReqObj();

Neueste Einträge abfragen
Bei der Abfrage nach den neuesten Einträgen wird die LatestID in der http-Anfrage mit übergeben. Diese ist eine globale Javascript-Variable, da ihr Wert aus Funktionen heraus geändert wird. Initialisiert wird sie mit dem Wert null. Die URL zu dem Skript ajaxchat.php müssen Sie natürlich an Ihre Umgebung anpassen.

Die Funktion getLatestEntries sieht wie folgt aus:

      //Funktion, mit der Anfragen nach neuen Einträgen gesendet wird
    function getLatestEntries() {
    //Anfrage senden, wenn Status der letzten Anfrage "completed" ist, bzw. "nicht
    initialisiert" (d.h. erster Aufruf)
    if (httpGetLatestEntries.readyState == 4 || httpGetLatestEntries.readyState
    == 0) {
    //Übergabe der latestID
    httpGetLatestEntries.open("GET","
    http://localhost/ajax/ajaxchat.php?
    action=getLatestEntries&latestID="+latestID, true);
    httpGetLatestEntries.onreadystatechange = handleHttpGetLatestEntries;
    httpGetLatestEntries.send(null);
    }
    }

Als Funktion, mit welcher der Browser die Serverantwort behandelt ist die Funktion handleHttpGetLatestEntries angegeben:

      //Behandelt die Serverantwort
    function handleHttpGetLatestEntries() {

    //wenn Anfrage den Status "completed" hat
    if (httpGetLatestEntries.readyState == 4) {
    //ermitteln der Antwort
    response = httpGetLatestEntries.responseXML.documentElement;
    //ermitteln der Messages; Überführung in ein Array
    messages = response.getElementsByTagName('message');

    //wenn es mindestens eine neue Nachricht hat, dann wird diese angezeigt!
    if(messages.length > 0) {

    for (var i=messages.length-1; i>=0; i--) {
    //Darstellung im Browser mit dem DOM
    showEntry= document.getElementById("showEntries");
    neuSpan = document.createElement('span');
    neuSpan.setAttribute('class','entry'); //CSS Klasse
    neuSmall = document.createElement('small');
    neuNameDate = document.createTextNode(messages[i].getElementsByTagName
    ('date')[0].firstChild.nodeValue + ': ' + messages[i].getElementsByTagName('name')[0].firstChild.nodeValue
    +': ');
    neuSmall.appendChild(neuNameDate);
    neuSpan.appendChild(neuSmall);
    neuSpan.appendChild(document.createElement('br'));
    neuNachricht = document.createTextNode(messages[i].getElementsByTagName
    ('nachricht')[0].firstChild.nodeValue);
    neuSpan.appendChild(neuNachricht);
    neuSpan.appendChild(document.createElement('br'));
    showEntry.insertBefore(neuSpan, showEntry.firstChild);
    }

    //Festlegung der neuen latestID; Zugriff auf die Werte über das DOM
    latestID = messages[0].getElementsByTagName('id')[0].firstChild.nodeValue;

    }
    //erneuter periodischer Aufruf (eine Art Rekursion)
    setTimeout('getLatestEntries();',3000); //Erneute Anfrage in drei Sekunden
    }
    }

Über die setTimeOut-Funktion wird nun unsere periodische Überprüfung nach neuen Einträgen durch den Aufruf der Funktion getLatestEntries alle drei Sekunden initialisiert; dabei handelt es sich um eine Art „verzögerte Rekursion“.

Neue Nachrichten eintragen
Die JavaScript Funktionen, über welche neue Einträge an den Server gesendet werden, sind ähnlich aufgebaut, wie die vorherigen Funktionen. Mit der ersten Funktion createNewEntry wird die http-Anfrage erzeugt, wobei ihr die Werte für die Felder name und nachricht aus dem HTML-Formular übergeben werden.

      //neue Nachricht auf dem Server erzeugen; die Übergabe der Werte
    erfolgt über das HTML Formular
    function createNewEntry(name, nachricht) {

    //Anfrage senden, wenn Status der letzten Anfrage "completed" ist, bzw. "nicht
    initialisiert" (d.h. erster Aufruf)
    if (httpCreateNewEntry.readyState == 4 || httpCreateNewEntry.readyState ==
    0) {
    //URL für HTTP Anfrage; muss angepasst werden
    url = 'http://localhost/ajax/ajaxchat.php?action=createNewEntry&name='
    + name + '&nachricht=' + nachricht;
    httpCreateNewEntry.open("GET", url, true);
    httpCreateNewEntry.onreadystatechange = handleHttpCreateNewEntry;
    httpCreateNewEntry.send();
    }
    }
      //behandelt die Antwort des Servers
    function handleHttpCreateNewEntry() {
    if (httpCreateNewEntry.readyState == 4) {
    //nachdem eine neue Nachricht erfolgreich erzeugt wurde, wird diese angezeigt
    getLatestEntries();
    }
    }

Nachdem die Daten erfolgreich in der Datenbank hinterlegt wurden, wird die Funktion getLatestEntries() wiederum aufgerufen, um sicherzustellen, dass die neue Nachricht auch sofort angezeigt wird.

Das Frontend
Nachdem nun die AJAX-Engine aufgestellt ist, wenden wir uns dem Frontend zu. Auch hier ist JavaScript gefragt. So können die Eingaben über JavaScript überprüft werden; die Darstellung der Nachrichten kann über eine CSS-Klasse beeinflusst werden.

Das Formular
Das Formular ist simpel gehalten; es gibt lediglich Eingabefelder und einen Submit-Button:

      <form name="form1" method="post" action="">
    <p>Name<br />
    <input type="text" name="name" id="name">
    <br />
    <br />
    Nachricht<br />
    <input type="text" name="nachricht" id="nachricht">
    </p>
    <p>
    <input type="submit" name="Submit" value="Submit" onclick="checkInput(document.form1.name.value,
    document.form1.nachricht.value)" >
    </p>
    </form>

Nachdem man auf den Submit-Button geklickt hat, wird die JavaScript-Funktion checkInput aufgerufen; an diese werden die eingegebenen Daten weitergegeben. In ihr wird die Eingabe überprüft.

Eine solche Überprüfung könnte im einfachsten Fall wie folgt aussehen:

      function checkInput(name, nachricht) {
    if(name != "" && nachricht != "") {
    //Falls alles OK ist, kann der neue Eintrag erzeugt werden
    createNewEntry(name, nachricht);
    }
    else {
    alert("Bitte sowohl einen Namen, als auch eine Nachricht eingeben!");
    }
    }

Die Darstellung der Ergebnisse kann mit Hilfe von CSS beeinflusst werden. Dazu greifen wir auf die über das DOM zugewiesene CSS-Klasse „entry“ zurück; über folgende Zeilen könnte man die Darstellung im Browser ein wenig ansehnlicher machen.

      <style type="text/css">
    .entry {
    width: 100%;
    background-color: #F5F5F5;
    border: 1 dotted #666666;
    font-family: Verdana;
    }
    .entry#small {
    color: #CCCCCC;
    }
    </style>

Screenshot

AJAX Chat in der konkreten Anwendung

Der Aufruf des Skriptes kann über einen onLoad-Event im Body-Tag der HTML-Seite (<body onload=“getLatestEntries()“>), in welcher das Formular eingebettet ist, eingeleitet werden; dabei wird die Funktion getLatestEntries das erste mal aufgerufen, wodurch die setTimeOut-Endlosschleife gestartet wird.

Alles in allem, war es ein langer Weg von der Idee zur konkreten Umsetzung. AJAX bedeutet nicht nur, dass die Webseiten in Zukunft in ihrem Verhalten normalen Desktop-Anwendung ähneln können, sondern bringt auch viel Arbeit mit sich. Vor allem das Zusammenspiel von JavaScript und XML klappt bislang nicht immer reibungslos.

Das hier vorgestellte Beispiel besitzt zudem einen Bug, der nicht verschwiegen werden darf. Auf der JavaScript Seite kann nicht mit Umlauten oder anderen Sonderzeichen umgegangen werden; diese verursachen einen Fehler. Alle Dateien gibt es wie immer als Paket zum Download. ™

Zur Problematik der Sonderzeichen ein Feedback von Rene Gade: Ich arbeite seit längerem mit XMLHTTPRequest, um das Umlaut-Problem in den Griff zu bekommen, habe ich eine Funktion zwischen geschaltet, die die Variablen-Werte kodiert und zum Server schickt.

Beispiel:

    //url = Ziel
    //param = Name der Variable
    //val = Wert der Variable
    function AddParam(url, param, val)
    {
    var qs = (url.indexOf("?") > -1 ) ? "&" : "?";
    //kodiere Wert (Umlaute und Sonderzeichen )
    qs += param + "=" + encodeURI(val);
    return url + qs;
    }

Auf dem Server müssen die Werte je nach Sprache wieder dekodiert werden.

Erstveröffentlichung 19.09.2005

Von Thiemo Fetzer

Thiemo Fetzer lebt seit 2008 in London und promoviert dort im Fachbereich "Entwicklungsökonomie" an der London School of Economics. Zuvor hat er Wirtschaftswissenschaften, Mathematik und Informatik in Magdeburg und Ulm studiert.

10 Antworten auf „AJAX in der Praxis: Ein einfacher Chat“

Eine minimale Fehlerkorrektur im Code sorgt dafür, dass der Code auch mit Firefox und Chrome funktioniert.

Die Stelle:

mit

ersetzen.

Die Anpassung des Buttons kann mit CSS durchgeführt werden.

Hallo,

im InternetExplorer funktioniert der Chat einwandfrei! Aber im Firefox nicht – er speichert die Message nur in einen Intervall von 10-50 Minuten, und dann passiert erst mal lange Zeit gar nichts – bis es dann nach 10-50 Minuten erneut funktioniert… das setzt sich dann so fort.

Ich habe auch schon die Übertragung in der ajaxchat.htm von GET auf POST verändert, das ändert nichts an dem Firefox-Fehler. Woran liegt das?

function getLatestEntries($latestID) {

$query = „SELECT id, name, nachricht, date FROM ajaxchat WHERE id > ‚“.$latestID.“‚ ORDER BY id DESC LIMIT 20″;

oder

$query = „SELECT id, name, nachricht, date FROM ajaxchat WHERE id > ‚$latestID‘ ORDER BY id DESC LIMIT 20“;

ich habe mir die direkten datein angeguckt, die zum download da sind, angepasst, aber es funtzt nich im ansatz. ist das überhaupt dazu gedacht? ich versuche mir jetzt das selbst zusammenzubasteln, bezweifle aber meinen erfolg. (nie etwas mit ajax gemacht, nur php)
über tipps & hilfe bin ich dankbar^^
mfg

Ist das aber nicht trotzdem sehr auslastend für die datenbank? Kann man das ajax nicht auf der serverseite laufen lassen? Sodass ajax und DB auf einem server laufen? Damit könnte man nämlich hunderte anfragen pro eingeloggten nutzer sparen…

Schreibe einen Kommentar zu Nils Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.