1 - Secret Lake

Werbung
1. Funktionen und Techniken
Die Shoutbox, die am Ende dieser Anleitung entstanden sein wird, besteht aus validem
XHMTL, schick gemacht mit CSS. Funktionen zum Abrufen und Speichern der einzelnen
Nachrichten werden auf dem Webserver, der unbedingt erforderlich ist, mit PHP realisiert.
Sämtliche Daten werden in einer mySQL-Datenbank gespeichert. Mit AJAX, genauer mit
JavaScript und dem XmlHttpRequest-Objekt, entsteht das Herzstück der Anwendung. Der
Chat läuft auf allen aktuellen Versionen von Internet Explorer, Firefox und Opera.
2. Das Front-End
Die Shoutbox selbst ist schnell gezimmert: Ein paar Block-Elemente, Eingabefelder und ein
Button zum Absenden reichen vollkommen aus. Grundsätzlich müssen zwei Bereiche
eingerichtet werden: Einerseits ein Bereich zum Anzeigen bisheriger Nachrichten, ein
weiterer zum Eingeben von Name und Nachricht. In diesem Tutorial soll das folgendermaßen
aussehen:
Oder in Form von w3c-konformem XHTML-Code:
<div id="asb_container">
<div id="asb_contentwrap">
<div id="asb_content">
Shoutbox wird geladen ...
</div>
</div>
<div id="asb_inputwrap">
<div id="asb_input">
<form action="" name="frmshoutbox" onsubmit="saveData(); return false;">
<b>Name:</b><br /><input class="text" type="text" name="txtname" value=
"" /><br />
<b>Nachricht:</b><br /><input class="text" type="text" name="txtmessage
" value="" /><br />
<input class="button" type="submit" name="btnsend" value="Senden" />
</form>
</div>
</div>
</div>
Einzelne Elemente wurden mit eindeutigen Namen versehen, damit es CSS und JavaScript
leichter fällt, Teile der Shoutbox zu identifizieren und anzusprechen - mehr dazu gleich. Die
Ausgabe bisheriger Nachrichten erfolgt in einem div mit der id asb_content. Die Eingabe
erfolgt über eine form mit zwei Éingabefeldern. Drückt man in selbigen die Enter-Taste oder
betätigt man den Senden-Button, wird die Nachricht verschickt.
Im momentanen Zustand sieht die Shoutbox noch relativ nackt aus. Ein Ansatz von XHTML
war es ja schließlich auch, schmückende Elemente aus HTML zu entfernen und nur noch
strukturierend tätig zu sein. Nun darf CSS seine Macht entfalten, mit diesem Stylesheet:
form {
margin: 0px;
}
#asb_container {
border: 1px dashed #B52021;
width: 160px;
}
#asb_contentwrap {
font: 8pt Arial;
height: 200px;
background-color: #DDDDDD;
overflow: auto;
}
#asb_content {
margin: 5px;
}
#asb_content .name {
color: #555555;
font-weight: bold;
padding-right: 5px;
}
#asb_inputwrap {
font: 8pt Arial;
}
#asb_input {
margin: 5px;
}
#asb_input .text {
border: 1px solid #888888;
font: 8pt Arial;
width: 146px;
background-color: #f4f4f4;
}
#asb_input .button {
width: 50px;
margin-top: 4px;
background-color: #B52021;
font: bold 8pt Arial;
color: white;
border: 1px solid #888888;
}
Grundsätzlich sind zum CSS-Teil nur zwei Dinge anzumerken: 1. Dem form-Tag wurde der
hauseigene Außenrand entzogen, der sich gern mal unansehnlich ausbreitet. 2. Die Definition
asb_contentwrap beinhaltet overflow: auto. Damit wird automatisch eine vertikale Scrollbar
angezeigt, sollten einmal die darzustellenden Nachrichten nicht in die feste Größe der
Shoutbox passen.
3. Nachrichten speichern
Sämtliche Nachrichten werden in einer mySQL-Datenbank gespeichert, eine einzige Tabelle
genügt, nennen wir sie treffenderweise shoutbox. Sie enthält die Felder name, message und,
wie sich das gehört, id als Primärschlüssel. In SQL ausgedrückt, sieht das so aus:
CREATE TABLE shoutbox (
id int(11) NOT NULL auto_increment,
name varchar(50) NOT NULL default '',
message tinytext NOT NULL,
PRIMARY KEY (id)
)
4. Ein- und Auslesen von Nachrichten
Zwei PHP-Skripten kümmern sich nun darum, die eben erstellte Datenbanktabelle mit
Nachrichten zu füllen und selbige auch wieder zu lesen. Ein drittes Skript beinhalt alle
Einstellungen für den serverseitigen Bereich der Anwendung, nennen wir es config.php:
<?
//Datenbank-Einstellungen
$dbhost='localhost';
$dbuser='username';
$dbpass='password';
$db='db1';
$dbtable='shoutbox';
//Anzahl der auszugebenden Nachrichten
$messages_count = 10;
?>
$messages_count soll das Ausgeben bereits bestehender Nachrichten auf eine gewisse Anzahl
einschränken. Ansonsten würde getdata.php bei jeder Anfrage alle Datensätze ausgeben und
unnötig Traffic verursachen. Erschwerend kommt hinzu, dass getdata.php relativ oft
aufgerufen wird, mehr dazu gleich.
Das Skript baut, basierend auf den Einstellungen aus config.php, eine Datenbankverbindung
auf. Mittels SQL werden die neuesten Nachrichten aus der Datenbanktabelle gefischt und
ausgegeben. Die Ausgabe erfolgt mit Hilfe von HTML-Tags. Das ist nicht unbedingt die
professionellste Variante, schließlich kümmert sich ein serverseitiges Datenbankabfrageskript
um die optische Gestaltung einer browserseitigen Angelegenheit. Aber wollen wir es nicht
komplizierter machen, als es sein muss.
Übrigens: Manch’ Browser packt getdata.php bei einem Aufruf in den Cache und weigert
sich, bei einem nächsten Aufruf die Datei erneut vom Server zu laden um die lokal gecachte
Version zu nutzen. Das Todesurteil für jede chatähnliche Anwendung. Daher beginnt das
Skript mit einigen Funktionen, die den Browser von diesem Blödsinn abzuhalten.
<?php
//Cachen verhindern
header("Expires: Sat, 05 Nov 2005 00:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
//Einstellungen laden
include("config.php");
//Verbindung zu mySQL aufbauen
$dblink = mysql_connect($dbhost, $dbuser, $dbpass);
if (!$dblink) {
die('Keine Verbindung zur Datenbank möglich.');
}
//Datenbank auswählen
$dbselected = mysql_select_db($db, $dblink);
if (!$dbselected) {
die ('Kann Datenbank nicht erreichen.');
}
//Datensätze ermitteln
$dbresult = mysql_query("SELECT * FROM $dbtable ORDER BY ID DESC LIMIT 0,$m
essages_count");
if (!$dbresult) {
die('Ungueltige SQL-Query.');
}
//Datensätze auslesen und formatieren
while ($row = mysql_fetch_assoc($dbresult)) {
echo "<span class="name">".$row["name"].":</span>";
echo $row["message"]."<br />";
}
//Verbindung zur Datenbank schließen
mysql_close($dblink);
?>
setdata.php schreibt neue Nachrichten in die Datenbanktabelle shoutbox. Dabei ist zu
beachten, dass die Daten per POST-Methode aus dem Eingabebereich der Shoutbox
übertragen werden.
<?php
//Einstellungen laden
include("config.php");
//Verbindung zu mySQL aufbauen
$dblink = mysql_connect($dbhost, $dbuser, $dbpass);
if (!$dblink) {
die('Keine Verbindung zur Datenbank möglich.');
}
//Datenbank auswählen
$dbselected = mysql_select_db($db, $dblink);
if (!$dbselected) {
die ('Kann Datenbank nicht erreichen.');
}
//Neuen Datensatz speichen
$result = mysql_query("INSERT INTO $dbtable (name, message) VALUES ('".$_PO
ST["name"]."','".$_POST["message"]."')");
if (!$result) {
die('Ungueltige SQL-Query');
}
//Verbindung zur Datenbank schließen
mysql_close($dblink);
?>
5. Die AJAX-Schaltzentrale
Die lästigen Vorarbeiten sind getan, kommen wir nun zum Herzstück der Shoutbox. AJAX
nimmt die Eingaben aus den Eingabefeldern entgegen und versorgt setdata.php mit der neuen
Nachricht. AJAX sorgt aber auch dafür, ständig die neuesten Nachrichten auf dem Schirm zu
holen - vielleicht ja von anderen Nutzern der Shoutbox. Dafür wird regelmäßig getdata.php
abgerufen. Das beste daran: Die Seite, auf der die Shoutbox enthalten ist, muss dafür nicht
ständig neu geladen werden, wie es ohne AJAX nötig wäre. Man stelle sich nur einmal vor,
die Shoutbox befände sich nicht allein auf einer Website, sondern zusammen mit anderen
Texten, Bildern, usw. Das ist wohl der wahrscheinlichere Fall, nicht wahr?! Jeder Besucher
würde wohl bald flüchten, müsste er alle paar Sekunden ein Neuladen der kompletten Site
ertragen.
Bevor wir munter dank AJAX mit den PHP-Skripten auf dem Server kommunizieren können,
müssen Vorbereitungen getroffen werden. Das XMLHttpRequest-Objekt, Basis von AJAX,
muss beim Aufrufen der Seite erstellt werden, um es später nutzen zu können. Dazu wird eine
Instanz erstellt und der globalen Variable xmlHttp zugewiesen. Beim Internet Explorer ist
XMLHttpRequest ein ActiveX-Objekt, bei der Konkurrenz ist es Bestandteil des Browsers.
Hinzu kommt, dass das XMLHttpRequest-Objekt beim IE unterschiedlich angesprochen
werden kann. Diesen Umständen wird z.B: mit try-catch-Blöcken Rechnung getragen: Schlägt
eine Möglichkeit, XMLHttpRequest zu instanziieren, fehl, wird eben die nächste probiert.
Sollte das Erstellen der Instanz letzlich doch geglückt ist, werden die neuesten Nachrichten
vom Server geladen. Stand eben noch “Shoutbox wird geladen…” (siehe HTML-Template
oben) auf dem Schirm, breiten sich jetzt Namen und Texte aus - sofern vorhanden. Die
JavaScript-Funktion loadData kümmert sich darum. Damit immer wieder und wieder ein
Nachrichten-Update erfolgt, wird diese Funktion regelmäßig abgerufen. Dazu nutzen wir
setInterval.
//globale Instanz von XMLHttpRequest
var xmlHttp = false;
//XMLHttpRequest-Instanz erstellen
//... für Internet Explorer
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
xmlHttp = false;
}
}
//... für Mozilla, Opera, Safari usw.
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
//aktuelle Daten laden
loadData();
//alle 5 Sekunden neue Daten holen
setInterval("loadData()",5000);
Was genau passiert nun in loadData? xmlHttp wird beauftragt, die Datei getdata.php vom
Server abzurufen und bei Erfolg den Inhalt in der Shoutbox auszugeben. Über die Eigenschaft
readyState erfahren wir, wann genau dieser Moment eingetroffen ist. readyState hat dann den
Wert 4. Ändert sich diese Eigenschaft, tritt das Ereignis onreadystatechange ein, welches wir
mit einer Funktion (in diesem Beispiel in Form einer inline-Funktion) behandeln. Über die
Methode getElementById steuern wir unseren div-Tag mit dem Namen asb_content an und
verändern die Eigenschaft innerHTML. Dort stehen dann die neuen Nachrichten.
function loadData()
{
if (xmlHttp) {
xmlHttp.open('GET', 'getdata.php', true);
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
document.getElementById("asb_content").innerHTML = xmlHttp.res
ponseText;
}
};
xmlHttp.send(null);
}
}
Interessant an dieser Stelle: Das erste A in AJAX steht für Asynchronous. Das heißt,
sämtliche Abläufe auf der Website laufen unabhängig von unserer Anfrage an den Server
weiter. Liefe der Prozess synchron weiter, müssten die Abläufe auf der Website gestoppt
werden, bis die Daten komplett übertragen worden sind. Um aber dennoch die Kontrolle über
den Datentransfer zu behalten, nutzt man die Ereignisbehandlung.
Um eine Nachricht an den Server zu senden, versorgt die JavaScript-Funktion saveData das
Skript setdata.php per POST-Methode mit den Benutzereingaben. Wir nutzen POST, um den
Beschränkungen von GET (maximal 256 Zeichen Text) aus dem Weg zu gehen. Die Daten
werden in Form von Parametern weiter gegeben, die die Methode send des XMLHttpRequest
entgegen nimmt. savedata.php schreibt die erhaltenen Daten in die Datenbank.
Es gibt noch ein Problem: Normalerweise wird eine Seite neu geladen, wenn man in einem
HTML-Formular Enter drückt oder den Senden-Button betätigt. Das Neuladen wollen wir
natürlich unbedingt verhindern, dennoch nicht auf die Annehmlichkeiten etwa der Enter-Taste
verzichten. Das onsubmit-Ereignis einer form wird ausgelöst, sobald das Formular
abgeschickt werden soll. Da klinken wir uns ein und leiten an dieser Stelle zu unserer
Funktion saveData um. Diese verschickt die Daten und kehrt zur Behandlung von onsubmit
zurück. Da damit aber alles erledigt ist, was wir wollen, brechen wir einfach die weitere
Abarbeitung des Formulars ab. Dies erfolgt mit return false. Vielleicht haben ja einige diesen
“Hack” bereits ganz am Anfang im HTML-Teil entdeckt? Damit das Ganze dennoch wirkt,
wie von einem Formular gesendet, setzen wir mit der Methode setRequestHeader eine
entsprechenden Header. So kommt setdata.php mit den übergebenen Daten hunderprozentig
klar. Die Funktion saveData komplett:
function saveData()
{
if (xmlHttp) {
xmlHttp.open('POST', 'setdata.php');
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-formurlencoded');
xmlHttp.send('name='+document.frmshoutbox.txtname.value+'&message='+do
cument.frmshoutbox.txtmessage.value);
}
document.frmshoutbox.txtmessage.value = '';
document.frmshoutbox.txtmessage.focus();
}
Am Ende der Funktion wird dafür gesorgt, dass das Nachrichteneingabefeld geleert wird und
bereit für neue Eingaben ist. Um das zu vereinfachen, wird zu guter letzt noch der Fokus
dorthin versetzt.
6. Erweiterungsmöglichkeiten
Das obige Beispiel ist recht einfach gehalten und offen für Erweiterungen aller Art. Wie wäre
es etwa, noch Datum und Zeit jeder Nachricht anzuzeigen?
Ein echtes Highlight wäre zweifelsohne auch, diese AJAX-Applikation noch AJAXkonformer zu machen - indem getdata.php die Daten nicht als HTML-Schnippsel zurückgibt
sondern als XML-Datei. Dann müsste sich JavaScript darum kümmern, die XML-Daten zu
verarbeiten.
Herunterladen