Wie Flash – nur ohne Flash!

Das Erstellen einer Internetseite ist nach wie vor eine spannende Sache. Wir von codingfreaks haben eine Weile gebraucht, aber jetzt haben wir eine auf JavaScript aufsetzende Technik, die uns Ajax-Reloads inkl. Deeplinks ermöglicht. Jetzt können wir RIAs bauen, die sich anfühlen wie Flash, aber keines sind.

Links

Code-Download

Demo

Einführung

Zunächst einmal vorweg: das hier ist beileibe kein Artikel für HTML-Neulinge oder Javascript-Kiddies. Trotzdem werde ich ein paar Basis-Informationen vorweg geben, um einfach klar zu machen, was ich meine.

Das Problem, das hier behandelt wird entsteht, weil eine Internetseite in dem Moment, in dem man einen Link anklickt (also navigiert) einen Server-Request vornimmt. Im Browser wird der Ladebalken gezeigt, man verliert die Seite aus dem Browserfenster und kommt in der neuen Seite frisch an. Es gab bereits früher Techniken, um nur bestimmte Bereiche einer Seite neu zu laden (Frames). Diese sind allerdings vor allem aufgrund von Sicherheits-Risiken und Suchmaschinen-Problemen inwzischen sehr verpönt und sollten nicht genutzt werden.

Seitdem das Web-2.0 ausgerufen wurde ist eine weitere Technik in den Fokus gerückt: Ajax. Das Wort steht für „Asynchronous JavaScript and Xml“ und bedeutet, dass eine Komponente zwischen Browser und Webserver eingeklinkt wird (Proxy), die nun den Server-Request macht, wartet bis das Ergebnis kommt und selbiges einfach in das DOM (Document Objekt Model) der Seite im Browser injiziert. Da die Browser diese DOM-Manipulation inwzischen alle „Life“ anzeigen, sieht es so auch, als würde die Seite neu geladen werden, ohne dass der Ladebalken kommt und man behält als Programmierer die volle Kontrolle über die Seite.

Der nächste Gedanke liegt nun nah: Man nehme einen Link, lade die dahinter liegende Seite per Ajax und injiziere deren Code in einen Bereich der Seite. Fertig! Kein Ladebalken mehr bei Seitenwechseln. Wirkt wie Flash und man kann z.B. während des Seiten-Aufbaus irgendwelche Animationen zeigen oder was weiß ich.

Das Problem mit dieser Technik sind allerdings die sog. Deep-Links. Damit sind die Unter-Seiten der Hauptseite gemeint. Dieser Blog-Eintrag ist ein gutes Beispiel. Er hat den Deeplink „http://www.codingfreaks.de/2011/01/24/wie-flash-nur-ohne-flash/„. Der fett markierte Teil ist dabei die Information, die einem bei einer komplett JavaScript-basierten Navigation fehlt, weil man ja immer nur die Hauptseite per Ajax neu befüllt.

Das Fehlen von Deep-Links wirkt sich vor allem dann sehr schlimm aus, wenn man von Suchmaschinen indiziert werden möchte oder wenn ein Besucher einen interessanten Artikel bookmarken will. Mit anderen Wort: Man braucht die Deep-Links.

Lösung

Zunächst sei jedem ein Blick auf das Ergebnis empfohlen. Im codingfreaks Reloader haben wir die verwendeten Techniken in einem Beispiel zusammen gefasst.

Technisch gesehen wird zunächst einmal die Seite index.html geladen:

<!doctype html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>codingfreaks Reloader</title>
        <link rel="stylesheet" type="text/css" href="default.css" />
        <script src="jquery_1_4_4.min.js" type="text/javascript"></script>
        <script src="jquery_ui_1_8_8.min.js" type="text/javascript"></script>
        <script src="tools.js" type="text/javascript"></script>
	</head>
	<body>
    	<div id="menu">
        	<ul>
            	<a href="#"><li>Home</li></a>
                <a href="#/products"><li>Produkte</li></a>
                <a href="#/services"><li>Leistungen</li></a>
            </ul>
        </div>
        <div id="viewport">
            <div id="content1" class="content">
            </div>
            <div id="content2" class="content inactive">
            </div>
        </div>
	</body>
</html>
*
{
	font-family:Verdana, Geneva, sans-serif;
	margin:0;
	padding:0;
	color:black;
}

body
{
	padding:20px;
}

ul
{
	padding:10px;
	list-style-type:none;
	list-style-position:inside;
}

li
{
	float:left;
	display:block;
	margin-right:20px;
}

a
{
	display:block;
	line-height:1.6em;
	vertical-align:middle;
	float:left;
	text-decoration:none;
}

a:hover
{
	background-color:black;
	color:white;
}

#menu
{
	width:600px;
	height:45px;
	top:10px;
	margin:auto;
	border:1px solid black;
}

#viewport
{
	position:relative;
	width:600px;
	height:500px;
	top:100px;
	margin:auto;
	border:1px solid black;
	overflow:hidden;
}

.content
{
	position:relative;
	width:600px;
	height:500px;
}

.inactive
{
	opacity:.7;
	top:0;
	left:-600px;
}

Hier ist alles pures Html-5. In Zeile 6 wird die CSS-Formatierung eingeklinkt, die Zeilen 7 und 8 binden jQuery ein und Zeile 9 holt schließlich die eigentliche „Magie“. Sie lädt die tools.js:

// JavaScript Document
var storedHash = "___";
var isFirstRun = true;
var currentContent = "#content1";
var nextContent = "#content2";

function Navigate(url)
{
	$(currentContent).fadeOut(500, function() {
		$(nextContent).load(url, function() {
			$(nextContent).css({'display':'block', 'opacity':'.3'}).animate({ left:0 }, 1000, function() {
				var tmp = currentContent;
				currentContent = nextContent;
				nextContent = tmp;

				$(currentContent).animate({'opacity':'1'}, 500);
				$(nextContent).css('left', '-600px');
				$(nextContent).addClass('inactive');

				Load();
			})
		})
	});
}

function Load()
{
	// fade-in the current-content-div
	if (isFirstRun &&
		window.location.hash != storedHash)
	{
		isFirstRun = false;
		storedHash = window.location.hash;
		UrlChanged(storedHash);
	}
}

function UrlChanged(hashedUrl)
{
	pureUrl = hashedUrl.substr(2, hashedUrl.length - 2);
	if (pureUrl.length == 0 ||
		pureUrl == "#")
		Navigate('welcome.html');
	else
		Navigate(pureUrl.toLowerCase() + ".html");
}

$(document).ready(function() {
	Load();
});

if ("onhashchange" in window) { // event supported?
	window.onhashchange = function () {
		UrlChanged(window.location.hash);
	}
}
else { // event not supported:
	var storedHash = window.location.hash;
	window.setInterval(function () {
		if (window.location.hash != storedHash) {
			storedHash = window.location.hash;
			UrlChanged(storedHash);
		}
	}, 100);
}

So wirklich interessant ist ja eigentlich nur Listing 3. Wer sich mit der jQuery-Synthax nicht auskennt, ist jetzt natürlich ein wenig verloren, aber ohne gewisse Frameworks geht es einfach nicht. Jeder, der sich dafür interessiert sei auf jquery.com verwiesen.

Im Script geht es genau genommen in Zeile 48 los. Mit dieser Zeile registrieren wir einen Event-Handler inkl. der typischen anonymen Methode. Ausgesprochen also: Sobald das Dokument fertig geladen ist, rufe die Methode „Load“ auf. In Wahrheit ist also Load unser Einstiegspunkt.

Load in Zeile 26 erfragt nun, ob es sich um den ersten Aufruf der Seite handelt (was es aus einer Member-Variablen erfährt) und ob window.location.hash ungleich dem Inhalt der Variablen storedHash ist. window.location.hash ermittelt die aktuelle Adresse des Browsers und schneidet den Domain-Anteil weg. Aus „codingfreaks.de/test“ behält hash nur noch „test“ übrig. Nach einigem Variablen-Rumgeschubse rught Load dann „UrlChanged“ (Zeile 38) auf.

UrlChanged ist dafür zuständig, immer zu reagieren, wenn sich die URL des Browsers ändert. Damit wir auch mit bekommen, wenn ein User oben in der Adresszeile etwas ändert, sorgen die Zeilen 52-65 dafür, dass entweder das „onHasChange“-event des Browsers auf UrlChanged umgebogen wird oder dieser Aufruf notfalls mittels eines Timers. Nicht alle Browser(versionen) unterstützen „onHasChange“.

Der Trick mit den Links

UrlChanged hat nur die Aufgabe, zu prüfen, welche Url eigentlich aus der gehashten erzeugt werden soll. Dabei machen wir uns einen Trick der Links in Html zu nutzen. Jeder Link, der mit einem „#“ beginnt, wird nicht als externer sondern als Links auf der gleichen Seite gewertet. Der Browser fängt also nicht an, zu laden. Das ist genau das, was wir brauchen. Wir setzen die Links einfach mit vorrangestelltem „#“ und erzeugen so unsere Deep-Links, die von außen ohne weiteres durchgereicht werden können.

Auch google ist zufrieden, weil es in den <a>-tags in Listing 1 alles findet, was es braucht (Zeilen 14-16).

Zum Schluss hilft uns die Methode Navigate, das ganze auch wirklich mal zum Navigieren zu bringen. Diese nutzt im Kern die Methode „Load“ von jQuery. Die Load-Methode öffnet eine Server-Seite und injiziert deren Ergebnis-Html 1:1 in ein Element (in unserem Fall eines der beiden divs mit der Klasse „content“). Ich habe nur deshalb zwei divs genommen, damit es etwas spannender wird und wir ein wenig Animation beim „Seitenwechsel“ haben.

Resumé

Wir sind jedenfalls froh, diese relativ einfache Technik für uns entdeckt zu haben. Man müsste jetzt eigentlich noch einen richtigen JavaScript-Manager dafür schreiben und den dann immer wieder verwenden, so ist es aber zumindest erstmal funktional und leicht verständlich. Viel Spaß beim Benutzen!

2 Antworten auf „Wie Flash – nur ohne Flash!“

  1. Danke für diesen nahezu genialen Code!
    Eure Demo läuft ja auch im Opera, wenn ich das ganze bei mir einbaue, läuft es da nicht mehr.
    Kann mir vll wer sagen warum?
    Vielen Dank!

Schreibe einen Kommentar

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

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.