AJAX: Ein einfacher Chat wie bei Facebook und co!

AJAX - Workshop Chat Anwendung Tutorial
Ein Online-Chat braucht nicht unbedingt Java, wie Facebook und co zeigen. 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. Wir zeigen Ihnen, wie es geht!

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
?>


Das PHP-Skript, der gesamte Backend ist
noch relativ harmlos. Bei unserer AJAX-Engine wird es kniffliger.


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 diese wird, sofern die Anfrage erfolgreich
war, die XML-Daten vom Server in ein Array (messages) überführt und im Browser
dargestellt. Dies findet jedoch nur statt, wenn es auch neue Nachrichten zum
Anzeigen gibt (wenn messages.length >0), das heißt wenn die Anfrage an das
PHP-Skript auch eine neue Nachricht zurückgegeben hat. Ist dies der Fall, wird
die latestID nach der Darstellung der neuen Nachrichten im Browser aktualisiert.
Da dies aus einer Funktion heraus geschieht, wird klar, weshalb latestID eine
globale Variable sein muss.


Ü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();
}
}



Die Antwort vom Server wird über die Funktion handleHttpCreateNewEntry
abgearbeitet; diese ist relativ kurz:


//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>


So könnte das Ergebnis im Browser
aussehen:


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.

(tf)
Dieser Artikel ist ebenfalls bei Dr.Web erschienen

This entry was posted in AJAX, PHP. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *