Sonntag, 13. April 2008

Mit AJAX HTML nachladen

Vorgeschichte

Wer das Vorgeplänkel überspringen will, klickt hier.

Ein Kunde von mir war noch nicht 100%ig zufrieden. Da das normal nicht sein kann, fragte ich nach, was ihn denn so stört. Das wiederum ist ein schönes Beispiel für diesen Beitrag. Also: Er (Kunde) hat auf seiner Webseite eine Art Galerie eingearbeitet. Standardprogramm: ein großes Bild und eine Reihe kleiner Thumbnails daneben. Die Box mit den Thumbs ist mit einem "overflow:hidden" ausgestattet und per JavaScript scrollbar. Das Problem ist nun, dass bei einem Klick auf einen Thumbnail die Seite mit dem gewünschten Bild komplett neu geladen wird. Die Scrollposition in der Thumbnailbox ist dadurch natürlich weg. Man muss sich also wieder mühsam zum nächsten Bild weiterscrollen. Gut, an sich ist das ja kein Problem, denn das war eine Änderung am Code, die vorher nicht abgesprochen war. Kann man also zusätzlich berechnen. Wie auch immer: Was tun? Das ursächliche Problem ist ja, dass die Seite beim Mausklick neu geladen wird. Dann muss man das halt verhindern. Und wie bekommt man dann die neuen Inhalte nachgeladen? Natürlich per AJAX.

Ich bin Informatiker, und wir sind ja grunsätzlich erstmal faule Leute. Wir wollen immer soviel wie möglich mit so wenig Einsatz wie nötig erreichen. Hier kommt jQuery ins Spiel, ein JavaScript Framework mit dem schönen Werbeslogan "The Write Less, Do More, JavaScript Library". Hört sich gut an. Mal sehen, was das so kann. Was muss ich machen?

HTML von einer anderen Seite laden/importieren

  1. Die Links im Menü modifizieren
  2. Den neuen Content nachladen
  3. Den alten Content mit dem Neuen ersetzen
Der Plan ist, die komplette Seite zu laden und dann eine ID Es ist hier vermutlich am sinnvollsten, wenn ich gleich das Endergebnis hier hinschreibe:


$(document).ready(function() {

$("#pic_menue a").bind("click", function(e){
$("#contentContainer").html('<div style="margin-top:25%;margin-left:40%;"><img src="fileadmin/images/ajax-loader.gif" title="Inhalt wird geladen" alt="Inhalt wird geladen" /></div>');
$("#contentRight").html('');
var link = $(this).attr("href");
$("div.replaceLeft").load(link + " #contentContainer",function(){
$("#menuRight div.mycontent").load(link + " #contentRight");
});
return false;
});
});



Das wird angewendet auf eine Struktur, die in etwa so aussieht:



<div class="replaceLeft">
<div id="contentLeft">
<p>Hier steht Inhalt</p>
</div>
</div>



So. Was macht das jetzt genau? Die erste Zeile ist soweit noch klar: das Script wird ausgeführt, sobald das DOM "ready" ist. Nun müssen die Links mit einer Funktion belegt werden. Normalerweise würde man das für jeden Link einzeln über einen "onClick"-Handler erledigen. Hier gehts aber einfacher: die Links im Container mit der ID "#pic_menue" werden per bind() mit einer Funktion belegt. Die Funktion liefert übrigens false zurück, damit der eigentliche Link nicht doch noch geklickt werden kann.

Die nächste Zeilen (3 & 4) sind im Grunde kosmetischer Natur. Dort lade ich eine kleine Ladegrafik in den Hauptcontainer. "#contentRight" - das muss ich hier noch erwähnen - ist ein zweiter Contentcontainer, der auch noch nachgeladen werden soll. Den habe ich hier jetzt mal im Script dringelassen, um auch einmal das sog. Chaining zu dokumentieren, also das Ausführen mehrerer JavaScript-Funktionen hintereinander. "#contentRight" ist leider nicht immer belegt, weshalb ich ihn jeweils erstmal leeren muss.

Jetzt gehts ans Eingemachte: das Linkziel muss ausgelesen und in eine Variable geschrieben werden. Ok, das kann man auch direkt im Funktionsaufruf und ohne zusätzliche Variable "link" machen, aber dadurch wirds doch arg unübersichtlich.

Um die zu ersetzende Box liegt eine weitere Box mit der Klasse "replaceLeft". Über den Dollar-Operator spreche ich diese Box nun direkt an und führe darauf die Funktion "load()" aus, welche nichts anderes macht, als per AJAX die per Link übergebene Seite auszulesen und das als Parameter übergebene Element in das Elternelement zu schreiben. Oder auf Deutsch: wir laden "#contentRight" von der Seite "link" in "div.replaceLeft" hinein.

An sich wären wir hier schon fertig. Aber wie gesagt gibts auch noch eine weitere Box mit Inhalt, die auch nachgeladen werden muss. Dies kann dann direkt im Anschluss mit einem weiteren load()-Aufruf erfolgen. load() kann man als dritten Parameter eine sog. Callback-Funktion mitgeben, welche direkt im Anschluss aufgerufen wird. Also geben wir dem ersten load()-Aufruf einfach einen weiteren load()-Aufruf als Callback-Funktion mit und laden durch dieses Chaining-Verfahren ein zweites Element nach.

Fazit

Die Nachrüstung von AJAX-Funktionalität war in diesem Fall richtig einfach und ging fast ohne Modifikation des HTML-Codes vonstatten. Mit "fast" meine ich, dass ich beispielsweise um die Box "#contentLeft" noch eine weitere Box "replaceLeft" herumbauen und natürlich die JS-Dateien einbindne musste. Das sollte ein Laie aber auch noch hinbekommen.

Der Vorteil an dieser Lösung ist, dass das Ganze auch bei Besuchern ohne JavaScript - wie auch beispielsweise Suchmaschinen - noch funktioniert, denn die Links an sich bleiben ja unangetastet. Und vor allem: man braucht nur etwas mehr als eine Hand voll Zeilen, um diesen Effekt zu erzielen.

Ich bin selbst allerdings nicht unbedingt Freund von solchen Lösungen. Es sieht zwar schön aus und lädt evtl auch schneller. Aber umgekehrt verliert man hier die Möglichkeit, beispielsweise die Browser-History zu verwenden. Man bleibt ja immer auf derselben Seite stehen. Außerdem kann man so auch nur sehr schwer Bookmarks auf die wirklich gewünschte Seite setzen. Glücklicherweise sind die Links ja jeweils noch intakt und können per Rechtsklick ein einem neuen Tab oder Fenster geöffnet werden. Aber da kommt auch nicht jeder Besucher drauf. Daher sollte jeder für sich bzw. sein Projekt den Einsatz noch einmal überdenken. Es ist nett, hip, fix, aber braucht man das?

PS: Basierend auf den verschiedenen Browser-Interpretationen kann es hier vorkommen, dass die Darstellung des Codes in den Boxen verschieden dargestellt wird und etwa die Zeilennummern nicht passen. Benutzt dann zur Anzeige bitte einen normalen Texteditor, der nicht einfach so umbricht.

PPS: Mehr dazu unter dem Label AJAX

4 Kommentare:

achili hat gesagt…

Hallo Wagii, vielen Dank für diese Anleitung. Da ich noch Anfänger bin, tue ich mir, trotz deiner ausführlichen Darlegung, mit Zeile 7 schwer:
$("#menuRight div.mycontent").load(link + " #contentRight");
Welchen Bezug haben #menuRight und #div.mycontent zum Script.
Ich habe dein Beispiel so verstanden, das in #contentContainer die div #contentRight, #contentLeft und #pic_menu liegen, wobei #contentLeft zum Ajax-Austausch zusätzlich mit #replaceLeft umschlossen wurde.
Ist das soweit richtig?
Wo sich die div aus Zeile 7 im HTML-Code befinden müssten, ist mir unklar. Vielleicht ist es dir möglich das HTML-Codebeispiel etwas zu erweitern.

Gruß
achili

wagii hat gesagt…

Hi,
ich glaube, ich sollte den Artikel mal komplett überarbeiten. Der ist irgendwie Grütze ;)

Guck Dir mal den Artikel von gestern an.

Im Grunde ist es egal, was da wo liegt, so lang man die Teile richtig anspricht.

achili hat gesagt…

Hallo Wagii,

danke für die rasche Antwort. Wer lesen kann ist klar im Vorteil. Nachdem ich das Lesen noch ein bisschen geübt habe, ist es mir auch gelungen das Beispiel umzusetzen.
Dein Artikel ist wirklich super. Etwas verwirrend war anfangs der zweite Content. Aber du beschreibst es ja genau. Tolle Arbeit!! Den anderen Artikel schau ich mir gleich in Ruhe an.

Meinen Respekt
achili

wagii hat gesagt…

Mahlzeit,

es gibt Nachschlag!