Effekte mit JavaScript

Vor einiger Zeit hatte ich mootools entdeckt. Es handelt sich dabei um ein JavaScript Framework. Somit kann man mit wenigen Befehlen saubere, Browserunabhängige Effekte in seine Seiten einbauen. Was mir aber besonders gut an diesem Framework gefällt: Der HTML-Code ist sauber aufgeräumt und muß nicht den JavaScript-Code erhalten. Das JavaScript greift über IDs oder Klassen (oder andere Selektoren) auf die HTML-Elemente zu und fügt dann die Funktionen ein.

Ein schönes Beispiel dafür ist das Accordion. Auch die Fx.Slide Funktionen finde ich einfach nur genial.

Wichtig dabei ist mir aber, dass das ganze auch ohne JavaScript angeschaut werden kann. Das JavaScript dient nur dazu, um ein angenehmeres Surfen zu ermöglichen. Schließlich soll der Inhalt auch weiterhin ohne Barrieren erreichbar bleiben.

Meine Vorgehensweise daher:

  1. Ich überlege mir, welche Inhalte ich präsentieren möchte
  2. Baue dann einen semantisch logischen HTML-Code dafür auf
  3. Dann überlege ich mir, was für Effekte das Surfen auf der Seite angenehmer gestalten könnte. Beispielsweise durch Zusammenklappen von Kapiteln oder eine kategorisierte Tabelle mit Reitern versehen.
  4. Schließlich erstelle ich den benötigen JavaScript-Code, ergänze den HTML-Code gegebenenfalls mit IDs oder Klassen und binde das JavaScript entweder am Ende der Seite oder über eine eigenen JS-Datei in die Seite ein.
  5. Fertig.

Als Beispiel hierfür nehme ich mal die Episoden-Liste meines Episodenguides. Die Episoden sind nach Staffeln kategorisiert.

Als Spalten nehme ich Episodenkurzbezeichnung (z.B. 1×04 für 4. Episode der ersten Staffel), deutscher Titel und deutsche Erstausstrahlung. Jede Staffel ist ein eigener <tbody>-Tag.
Hier also der HTML-Code:

<table id="episodelist">
    <thead>
        <tr>
            <th>Nummer</th>
            <th>Titel</th>
            <th>Datum</th>
        </tr>
    </thead>
    <tbody id="season_1">
        <tr>
            <th colspan="3">1. Staffel</th>
        </tr>
        <tr>
            <td>1x01</td>
            <td>Der Mächtige</td>
            <td>07.09.1990</td>
        </tr>
        <tr> ... </tr>
    </tbody>
    <tbody id="season_2">
        <tr>
            <th colspan="3">2. Staffel</th>
        </tr>
        <tr> ... </tr>
    </tbody>
</table>

Ich habe innerhalb des <tbody> nochmal eine <th> eingebunden, welche als Zwischenüberschriften zu verstehen ist. Über den CSS-Selektor: tbody th kann ich diesen anders formatieren.

Der nächste Schritt ist jetzt, dass ich über JavaScript alle <tbody>s in der Tabelle finde, kennzeichne und die Tabs daraus generiere.

Ich mache das wie folgt:

var mytab;           // Element für die Tabs
var showtab = "";    // Gibt an, welcher Tab angezeigt werden soll
var countTabs = 0;   // Zählt die Anzahl der Tabs
var i = 0;           // Zählt Anzahl der Zeilen
var tabtitle = "";   // Titel des Tabs

// Erzeugt die Tableiste
tabs = new Element('div', {});

// Tabelle durchschauen
$('episodelist').getChildren().each(function(el){
	if (el.tagName == "TBODY")
	{
		if (showtab == "")
		{
			// Dies ist der erste Tab
			showtab = el.id;
		}
		...
		...
	}
});

// Tableiste oberhalb der Tabelle einfügen
tabs.injectBefore('episodelist');

Soweit ich weiß, könnte man auch CSS Selektoren verwenden, um eine Liste mit Elementen zu erhalten (z.B. #episodeliste tbody), aber wenn ich jetzt verschachtelte Tabellen verwende, dann würde das auch die falschen tbodys erwischen.

Der Funktion $ wird die ID der Tabelle („episodelist“) übergeben. Davon werden dann die Kinder geholt (.getChildren()) und schließlich führe ich für jedes (.each()) Kindelement (el) eine Funktion aus. Innerhalb dieser Funktion überprüfe ich, ob das Kindelement (el) auch wirklich ein tbody ist (el.tagname == „TBODY“).

Für jedes <tbody> muß ich nun einen Tab erzeugen und entsprechende Eigenschaften hinzufügen:

mytab = new Element('span', {
	'events': {'click': function() {switchTab('episodelist',el.id);}},
	'class': 'tab',
	'id': 'tab-' + el.id
});

Ich erzeuge das Tab hier als <span> und füge eine onclick-Ereignis hinzu. Dieser Tab wird nur angezeigt, wenn JavaScript aktiviert ist, daher kann ich hier auch die onclick verwenden.

Jetzt möchte ich dem Tab noch einen Titel geben. In meinen bisherigen Tabellen hatte ich dazu dem <tbody> noch ein title-Attribut zugeordnet, welches ich dann mit el.title ausgelesen haben. Aber ich möchte jetzt den Text aus dem <th> auslesen. Also müssen wir jetzt wieder jedes Kindelement durchschauen. Die Kinder von <tbody> sind natürlich <tr>s. Also muß ich fragen, ob das Kindelement vom <tr> ein <th> ist.

tabtitle = "";
i = 0;
el.getChildren().each(function(el2){
	if (el2.getElement('th'))
	{
		// Titel des Tabs auslesen
		tabtitle = tabtitle + el2.getElement('th').getText() + " ";
	}
	if (el2.getElement('th') && !(el2.getElement('td')))
	{
		// Tabellenzeile mit Titel verstecken
		el2.style.display = 'none';
	} else {
		// Zeilen mit tds zählen
		i++;
	}
}

// Text für den Tab erstellen
mytab.setText(tabtitle + "(" + i + ")");

// Füge Tab in die Tableiste ein
mytab.injectInside(tabs);

Wenn man jetzt noch lustig ist, kann man natürlich jeder zweiten Tabelle eine Klasse zuweisen und auch die ganze Zeile anklickbar machen und mit Hover-Effekt versehen. Das ganze ist dann im else-Ast, statt dem i++:

if (++i%2==0)
{
	el2.addClass("even");
} else {
	el2.addClass("odd");
}
if (el2.getElement('a'))
{
	el2.addEvent("mouseover", function(event)
	{
		el2.addClass("hover");
	});
	el2.addEvent("mouseout", function(event)
	{
		el2.removeClass("hover");
	});
	el2.addEvent("click", function(event)
	{
		location.href = el2.getElement('a').href;
		return false;
	});
}

Mögliche Stylesheets wären dann:

tr.odd td {
	background-color: #ffffff;
	color: #000000;
}
tr.even td {
	background-color: #cccccc;
	color: #000000;
}
tr.hover td {
	background-color: #cc99ff;
	color: #000000;
	text-decoration: underline;
}

So, damit sind wir eigentlich fast fertig. Jetzt fehlt noch die Funktion switchTab. Damit beim Starten auch nur der erste <tbody> angezeigt wird, rufe ich auch auch gleich die Funktion mit der Variable showtab auf.

switchTab('episodelist',showtab);

Und hier noch die switchTab-Funktion. Die Funktion erwartet zwei Werte. Zuerst die ID der Tabelle, dann die ID des ausgewählten <tbody>s.

function switchTab (mp,c)
{
	$(mp).getChildren().each(function(el){
		if (el.tagName == "TBODY")
		{
			if (el.id == c)
			{
				// aktuelle <tbody> anzeigen
				el.style.display = "";
				$('tab-' + el.id).className = "tab-current";
			}
			else
			{
				// andere <tbody>s verbergen
				el.style.display = "none";
				$('tab-' + el.id).className = "tab";
			}
		}
	});
}

Mal schauen, wann ich das ganze auch als praktisches Beispiel fertig habe ;-).