top of page
Logo der Online Agentur mdwp

AJAX ganz einfach - Digitaluhr für Drupal

Drupal und AJAX

Wer wollte noch darauf verzichten - Websites mit dynamischen Inhalten sind einfach schick. Ein Klick, eine Mausbewegung, und auf magische Weise wird z.B. ein Textblock eingeblendet, ein Bild ausgetauscht, ein Popup angezeigt und Ähnliches - ohne die Seite komplett neu aufbauen zu müssen. Das spart Ladezeiten und gibt dem Betrachter das Gefühl, mit einem ganz normalen Anwendungsprogramm zu arbeiten.

Zur Erzeugung von dynamischen Inhalten sind im wesentlichen zwei Techniken verbreitet: die Kombination von Javascript mit CSS, mit der z.B. ein verstecktes Seitenelement angezeigt und wieder ausgeblendet werden kann, sowie das sogenannte AJAX (Asynchronous JavaScript and XML), um das es hier gehen soll.

Bei AJAX geht es im wesentlichen darum, Teile einer Seite (z.B. ein div Element) komplett auszutauschen und die neue Version des Elements dabei per HTTP-Request vom Server zu holen. Das ist auch der Hauptunterschied zwischen den beiden Ansätzen: beim Javascript/CSS-Ansatz müssen die geänderten Inhalte (wenn auch versteckt) bereits auf der Seite vorhanden sein (oder per Javascript erzeugt werden). Kein Problem bei Textblöcken, aber man stelle sich mal eine kleine Slideshow mit 50 Fotos vor, von denen 49 versteckt sind - seltsam, warum braucht das so lange zum Laden?

Die reine Lehre von AJAX sieht vor, dass zum Aktualisieren eines Seitenelements ein XML-Objekt per HTTP-Request vom Server angefordert wird. Das vergessen wir am besten gleich wieder, in Drupal wird normalerweise mit einem pragmatischeren Ansatz gearbeitet: Drupal verwendet AJAX in Form von AHAH (Asynchronous HTML and HTTP) - ein etwas unglückliches Kürzel, gemeint ist dabei, dass Javascript per HTTP-Request HTML-Fragmente anstelle von XML-Objekten anfordert, das Kürzel sollte also besser AJAH heißen, aber sei es drum. Ich werde hier jetzt schlampigerweise einfach weiter von AJAX sprechen.

Halten wir kurz fest: bei AJAX wird eine Seite mit einem dynamischen Element komplett aufgebaut, dann geschieht irgendein Javascript-Event (Mausklick, Mausbewegung, Timer o.ä.), Javascript ruft per HTTP-Request das Element als HTML-Fragment neu vom Server ab und tauscht es auf der Seite aus - voilá!

Genug der Theorie, ran ans Werk: es soll ein kleines Modul entstehen, welches die aktuelle Uhrzeit in einem Block anzeigt und sekündlich per AJAX aktualisiert - armer Webserver! Das ist eigentlich relativ sinnfrei, da sich so etwas prima ohne AJAX mit reinem Javascript machen lässt, aber es ist ein hübsches Beispiel, um die Anwendung von AJAX auf einfachste Art zu demonstrieren. Fangen wir also mal an mit einem Modul namens CLOCK, das einfach nur die Uhrzeit in einem Block anzeigt:

Wir benötigen ein clock.module:

/** * Implementation of hook_block(). */ function clock_block($op = ‘list’, $delta = 0) { $block = array(); switch ($op) { case ‘list’: $block[0][‘info’] = ‘Digitaluhr’; break; case ‘view’: $block[‘content’] = clock_display(); break; } return $block; } /** * Display Clock */ function clock_display() {

$output = ‘ ‘ . date(‘H:i:s’) . ‘

’; return $output;

}

Und dazu ein clock.info:

name = ‘Clock’ description = ‘Block mit Digitaluhr’ core = 6.x

Dieses Modul können wir aktivieren, den erzeugten Block im Admin-Bereich einer Theme-Region zuordnen, und schon bekommen wir bei jedem Seitenaufruf die aktuelle Uhrzeit angezeigt. Aber um sie zu aktualisieren, muss die ganze Seite neu geladen werden. Nicht gerade sexy. Ein Fall für AJAX!

Wir benötigen also ein kleines Script, nennen wir es clock.js, welches die Uhrzeit sekündlich aktualisieren soll. Und wir müssen Drupal diesen Wunsch bekanntgeben. Dazu gibt es die Funktion drupal_add_js, die wir der Anzeigefunktion beifügen können:

// Hier liegt unser Modul auf dem Server $path = drupal_get_path(‘module’, ‘clock’); // Und hier liegt das Script dazu drupal_add_js ($path . ‘/js/clock.js’);

Ein wenig Design kann auch nicht schaden, machen wir die Ziffern doppelt so groß und stellen die Uhrzeit zentriert dar! Das geht analog durch die Angabe einer passenden CSS-Datei:

// Und die CSS-Datei zu unserem Modul drupal_add_css ($path . ‘/css/clock.css’);

Hier die clock.css :

clock {font-size:2em;text-align:center;}

Auf jeder Seite, die unseren Block enthält, wird nun im Header das entsprechende JavaScript und CSS eingebunden. Und jetzt das Spannende, nämlich AJAX:

Wir benötigen ein Script, das sekündlich die aktuelle Uhrzeit vom Server holt und den Inhalt des DIVs mit der ID “clock” aktualisiert. Was aber heißt “vom Server holen”? Per Javascript wird eine Seite (URI) aufgerufen, deren Ausgabe die gewünschte Information ist. Wir müssen also unser Modul entsprechend ergänzen. Seiten werden in Drupal über hook_menu definiert, als URI verwenden wir einfach “/clock/refresh”.

/** * Implementation of hook_menu(). */ function clock_menu() {

$items[‘clock/refresh’] = array( // Das ist die URI ‘page callback’ => ‘clock_refresh’, // Kann ruhig jeder sehen ‘access callback’ => TRUE, // Im Menü brauchen wir das nicht! ‘type’ => MENU_CALLBACK, );

return $items;

}

/** * Refresh Clock */ function clock_refresh() { $output = date(“H:i:s”); die($output); }

Huh, das klingt tödlich. Aber der PHP Befehl “die” oder alternativ “exit” sorgt einfach dafür, dass das laufende Skript beendet wird, Drupal also keine weiteren Seitenelemente mehr drumherum basteln muss. Wir erhalten die Uhrzeit, und nichts als die Uhrzeit.

Zum Testen einfach mal die Seite im Browser aufrufen (‘www.beispiel.de/clock/refresh’), und wer misstrauisch ist, werfe mal einen Blick in den Seitenquellcode!

Fehlt nur noch das Javascript! Drupal verwendet das wunderbare Javascript-Framework jQuery, und jQuery bietet den Befehl load(), mit dem sich Seitenelemente ganz einfach per HTTP-Request aktualisieren lassen. Damit wird das Aktualisieren des DIVs mit der Uhrzeit ein Klacks, nämlich mittels unserer Javascript-Datei clock.js:

function refresh() { // Holt die aktuelle Uhrzeit vom Server $(‘#clock’).load(‘/clock/refresh’); }

Und das Ganze soll jetzt noch sekündlich geschehen:

$(document).ready(function(){ // Ruft die Funktion ein mal pro Sekunde auf. setInterval (“refresh()”,1000); });

So, sehr schön, wir haben einen Block mit einer sekündlich aktualisierten Digitaluhr. Aber da wären noch zwei hübsche Fallstricke versteckt: was geschieht eigentlich, wenn der Benutzer JavaScript deaktiviert hat? Eine Frage, die man sich beim Arbeiten mit Javascript immer stellen sollte! Entweder wir zeigen dann nur die beim Laden der Seite aktuelle Uhrzeit an, oder es erscheint ein Hinweis an den Benutzer, dass er Javascript aktivieren soll. Für letzteres ändern wir die Ausgabe des Blocks ein wenig und passen das Script entsprechend an:

/** * Display Clock */ function clock_display() {

$path = drupal_get_path(‘module’, ‘clock’); drupal_add_js ($path . ‘/js/clock.js’); drupal_add_css ($path . ‘/css/clock.css’);

$output = “ Javascript erforderlich!

”; return $output;

}

$(document).ready(function(){ refresh(); setInterval (“refresh()”,1000); });

Wenn ein Benutzer Javascript deaktiviert hat, erscheint der Hinweis “Javascript erforderlich”, ansonsten wird (fast) sofort die aktuelle Uhrzeit eingeblendet. Da setInterval die angegebene Zeit erst verstreichen lässt, bevor es den Refresh auslöst, müssen wird das einmalig direkt veranlassen.

Den zweiten Fallstrick hält Drupal für uns bereit, und der ist ganz gemein:

Wir wollen natürlich eine gute Performance auf unserer Seite, und haben deshalb die Caching-Funktionen von Drupal aktiviert, insbesondere den Block-Cache. Alles sieht ganz normal aus, solange wir eingeloggt sind (Drupal cacht keine Inhalte für angemeldetete Benutzer). Aber nach dem Logout steht in unserem schönen Block plötzlich nur “Javascript erforderlich!”, und zwar in einfacher Schriftgröße und linksbündig! Ein Blick in den Seiten-Quellcode verrät uns: clock.js und clock.css sind verschwunden! Wie das denn? Ganz einfach: der Block-Cache von Drupal speichert die HTML-Ausgabe des Blocks, also nur den erzeugten Text. Die Funktionen zum Einfügen von JavaScript und CSS in die Seite werden nicht aufgerufen, und die Seite selbst weiß nicht dass die beiden Dateien hier benötigt werden. Dieses unerwünschte Verhalten gilt es abzustellen. Wir könnten jetzt den Block-Cache generell deaktivieren, aber sinnvollerweise tun wir das nur für unseren Digitaluhrblock, indem wir Drupal das Cachen einfach verbieten:

… switch ($op) { case ‘list’: $block[0][‘info’] = ‘Digitaluhr’; // Dieser Block darf nicht gecacht werden! $block[0][‘cache’] = DRUPAL_NO_CACHE; break; case ‘view’: …

Jetzt klappt alles wie gewünscht. Das Beispiel mit der Digitaluhr ist wie erwähnt etwas sinnfrei, aber mit dieser Methode lassen sich natürlich ganz andere dynamische Inhalte erzeugen. Der Schlüssel dabi ist im Prinzip die Refresh-Funktion mit dem Abbruch der Skriptausgabe. Hier kann man eine Uhrzeit ausgeben, aber z.B. auch Inhalte aus der Datenbank holen, Bilder zurückgeben, externe Daten anziehen, Eintragungen sortieren, ein Popup erzeugen etc - der Fantasie sind hier keine Grenzen gesetzt.

Zum Abschluss noch mal das komplette Modul und JavaScript:

clock.module:

/** * Implementation of hook_menu(). */ function clock_menu() {

$items[‘clock/refresh’] = array( ‘page callback’ => ‘clock_refresh’, ‘access callback’ => TRUE, ‘type’ => MENU_CALLBACK, );

return $items;

} /** * Implementation of hook_block(). */ function clock_block($op = ‘list’, $delta = 0) { $block = array(); switch ($op) { case ‘list’: $block[0][‘info’] = ‘Digitaluhr’; $block[0][‘cache’] = DRUPAL_NO_CACHE; break; case ‘view’: $block[‘content’] = clock_display(); break; } return $block; } /** * Display Clock */ function clock_display() {

$path = drupal_get_path(‘module’, ‘clock’); drupal_add_js ($path . ‘/js/clock.js’); drupal_add_css ($path . ‘/css/clock.css’);

$output = “ Javascript erforderlich!

”; return $output;

} /** * Refresh Clock */ function clock_refresh() { $output = date(“H:i:s”); die($output); }

clock.js:

function refresh() { $(‘#clock’).load(‘/clock/refresh’); } $(document).ready(function(){ refresh(); setInterval (“refresh()”,1000); });

Autor: Boris Böhne

http://www.boehne.com

bottom of page