Backbone.js-Tutorial: Die Merkliste

Backbone.js ist ein interessantes JavaScript-MVC, mit welchem ich mich seit etwa zwei Wochen beschäftige. Und nun möchte ich ein kleines Tutorial dazu präsentieren - wir bauen uns ein Merkliste. Das Konzept sieht wie folgt aus: Ständig laufen einem tolle Filme, Bücher und Spiele über den Weg, die man unbedingt noch sehen/lesen/spielen möchte, aber man merkt sie sich nie. Das ist der Punkt, wo die Merkliste ins Spiel kommt.
Bei der Programmierung habe ich mich stark an der Todo List Application orientiert, allerdings ist die Merkliste vom Funktionsumfang her schmaler, beinhaltet dafür aber einen Controller für das URL-Routing.
Aber nun zur Sache:
Als erstes benötigen wir den HTML-Teil.
HTML
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Backbone.js-Tutorial - Merkliste</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<ul id="nav"></ul>
<input type="text" placeholder="Gib einen Titel ein…" id="list_input" />
<ul id="list"></ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.6/underscore-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.3.3/backbone-min.js"></script>
<script src="js/backbone-localstorage.js"></script>
<script src="js/list.min.js"></script>
<script type="text/template" id="list-item-template">
<strong><%= title %></strong>
<span class="delete_item">x</span>
</script>
<script type="text/template" id="nav-template">
<a href="#/category/<%= title %>"><%= title %></a>
</script>
</body>
</html>
Für die Merklisten-App benötigen wir jQuery, Underscore.js, Backbone.js und die Backbone-Erweiterung Local-Storage, damit die Einträge im Browser gespeichert werden können. Des weiteren werden die Container für die Navigation und die Listeneinträge, sowie das Eingabefeld angelegt. Schlussendlich brauchen wir noch zwei Templates - eins für die Navigation, eins für die Liste.
Kommen wir nun zum JavaScript-Teil. Als erstes benötigen wir hier unser Model:
JavaScript
window.List = Backbone.Model.extend();
Als nächstes erstellen wir zwei Collections, eine für die Navigation, eine für die Listeneinträge:
JavaScript
window.NavCollection = Backbone.Collection.extend({
model : List
});
window.ListCollection = Backbone.Collection.extend({
model : List,
localStorage : new Store('List'),
getByCategory : function ( category )
{
return this.filter( function (item)
{
return item.get('category') == category;
});
}
});
In der Collection für die Listen-Einträge wird der Local Storage angemeldet und eine Funktion eingefügt, die es ermöglicht, die Einträge der Collection nach ihrer Kategorie zu filtern.
Kommen wir nun zum Controller der App:
JavaScript
window.ListController = Backbone.Controller.extend({
_navModel : new NavCollection([
{title : 'Filme'},
{title : 'Buecher'},
{title : 'Spiele'}
]),
_navViews : [],
_categoryModel : new ListCollection,
_inputView : null,
routes : {
'' : 'init',
'/category/:category' : 'getItems',
},
initialize : function ()
{
this._navModel.each( function ( item, i )
{
this._navViews[i] = new NavigationView({
model : item
});
}, this);
Backbone.history.start();
},
init : function ()
{
window.location.hash = '/category/Filme';
},
getItems : function ( category )
{
for ( view in this._navViews )
{
this._navViews[view]
.render()
.setClass();
};
if ( this._inputView == null )
{
this._inputView = new ListInputView({
model : this._categoryModel,
category : category
});
}
else
{
this._inputView.options.category = category;
this._inputView.model.trigger('refresh');
}
}
});
Als erstes werden - jeweils für die Navigation und die Liste - neue Instanzen der zugehörigen Collection erstellt und Platzhalter für die jeweiligen Views angemeldet. Danach werden die relevanten Pfade mit Funktionen verknüpft. In diesem Fall wird die Funktion init() ausgeführt, wenn kein Hash vorhanden ist, und die Funktion getItems(), wenn ein Kategorie-Hash vorhanden ist.
Als nächstes folgt die initialize-Funktion, welche als erstes beim Aufrufen des Controllers ausgeführt wird. Dabei wird das Navigation-Model mit den nötigen Einträgen versehen, danach für jeden Eintrag der Navigation-Collection eine View-Instanz erstellt und im _navView-Array gespeichert, sowie die Backbone.history-Funktion gestartet.
Als nächstes wird die init-Funktion definiert. Diese sorgt einfach nur dafür, dass die Kategorie "Filme" gewählt wird, indem der entsprechende Kategorie-Hash gesetzt wird. Die Kategorie ist hierbei willkürlich von mir gewählt.
Danach folgt die Definition der getItems-Funktion. In dieser werden als erstes die Views für die Navigationspunkte ge-rendert. Daraufhin folgt entweder die Initialisierung des Views für die Listeneinträge, oder - falls dies schon geschehen ist - das Überschreiben der aktuellen, dem View übergebenen Kategorie und das Neu-Aufbauen der Liste mit den entsprechenden Einträgen. Dabei kommt die Filter-Funktion der Listen-Collection zum Einsatz.
Nachdem nun Model, Collection und Controller vorhanden sind, geht es an die Views. Davon benötigen wir drei - einen für das Eingabefeld, einen für die Liste und einen für die Navigation. Fangen wir mit dem Eingabefeld an:
JavaScript
window.ListInputView = Backbone.View.extend({
el : $('#list_input'),
list : $('#list'),
events : {
'keypress' : 'createListItem'
},
initialize : function ()
{
_.bindAll(this, 'addListItem', 'addAllListItems');
this.model.bind('add', this.addListItem);
this.model.bind('refresh', this.addAllListItems);
this.model.fetch();
},
createListItem : function (e)
{
if ( e.keyCode == 13 )
{
this.model.create({
category : this.options.category,
title : this.el.val()
});
this.el.val('');
this.el.blur();
}
},
addListItem : function ( item )
{
var view = new ListItemView({model : item});
!view.model.length && this.list.append( view.render().el );
},
addAllListItems : function ()
{
this.list.empty();
_.each(this.model.getByCategory(this.options.category), function(item)
{
this.addListItem(item);
}, this);
}
});
Der ListInputView nimmt die Eingabe entgegen, erstellt eine neue Instanz des Listen-Eintrag-View und fügt füllt die Liste mit Einträgen. Die Funktion createListItem() erstellt einen neuen Eintrag in der Collection, wenn das Eingabefeld abgefeuert wurde. Über die Angabe this.model.bind('add', this.addListItem); in der initialize-Funktion wird gesorgt, dass daraufhin die Funktion addListItem() aufgerufen wird, die für den neuen Eintrag in der Collection eine Instant des Listen-Eintrag-View erstellt und diesem den Befehl render() mit auf den Weg gibt. Über die Funktion addAllListItems() wird einerseits sichergestellt, dass die Liste geleert wird, bevor neue Einträge nach einem Kategorie-Wechsel reingladen werden, und andererseits, bei Übergabe einer Collection mit mehreren einträgen, für jeden Eintrag die Funktion addListItem() ausgeführt wird.
Als nächstes kommen wir zum Listen-Eintrag-View:
JavaScript
window.ListItemView = Backbone.View.extend({
tagName : 'li',
className : 'list_item',
tmpl : _.template( $('#list-item-template').html() ),
events : {
'click .delete_item' : 'removeItem'
},
render : function ()
{
$(this.el).html( this.tmpl( this.model.toJSON() ));
return this;
},
removeItem : function ()
{
this.model.destroy();
$(this.el).fadeOut( function()
{
$(this).remove();
});
}
});
Hier wird das Element vom Standard (div) auf li gesetzt, die gewünschte CSS-Klasse gesetzt, das Template für den Eintrag angemeldet, ein Klick-Event mit der Funktion removeItem() verknüpft und anschließend die Funktionen render() und removeItem() definiert. Erstere fügt die Daten des Collection-Eintrags in das Template ein, letztere löscht den View und den dazugehörigen Collection-Eintrag.
Schlussendlich benötigen wir noch einen View für die Navigation:
JavaScript
window.NavigationView = Backbone.View.extend({
tagName : 'li',
tmpl : _.template( $('#nav-template').html() ),
hash : function ()
{
return window.location.hash.replace('#/category/', '');
},
render : function ()
{
$('#nav').append($(this.el).html( this.tmpl( this.model.toJSON() ) ));
return this;
},
setClass : function ()
{
var curHash = this.hash();
this.el.className = ( curHash == $(this.el).find('a').text() ) ? 'current' : '';
}
});
Neu und interessant ist hier die Funktion setClass(). Diese sorgt nach dem Rendern der Navigation, dass der aktuelle Reiter die Klasse "current" bekommt.
Um die Listen-App nun zum Laufen zu bringen, erstellen wir eine neue Instanz des Controllers:
JavaScript
var listApp = new ListController();
Der ganze Spaß wird in eine anonyme jQuery-Funktion geschrieben, damit die App gestartet wird, sobald das DOM geladen ist.
Das war es auch schon. Ich hoffe, meine Erklärungen sind einigermaßen nachvollziehbar. Falls nicht, nutzt auf jeden Fall die Kommentar-Funktion. Des weiteren würde ich mich natürlich über Anregungen und Verbesserungsvorschläge freuen, da ich ja doch ein Neuling in Sachen Backbone.js bin. Ansonsten noch der Hinweis, dass bei Gefallen natürlich gerne regen Gebrauch von den unten stehenden Social-Media-Buttons gemacht werden kann.
Vielen Dank!
8Bit-Style-Navigation mit Fly-Out-Menus
Heute möchte ich kurz zeigen, wie man mit etwas HTML, CSS und ein paar kleinen GIF-Grafiken eine pixelige Seiten-Navigation im 8Bit-Stil baut. Außerdem benutzen wir etwas jQuery-Magic, um der Navigation noch Fly-Out-Menus zu spendieren.

Beginnen wir wie gewohnt mit dem HTML-Teil:
HTML
<div id="nav">
<ul>
<li class="top">
<a href="index.html">
<strong>Home</strong>
</a>
<div class="sub">
<div>
<ul>
<li>
<a href="#">Sub-Item 1</a>
</li>
<li>
<a href="#">Sub-Item 2</a>
</li>
<li>
<a href="#">Sub-Item 3</a>
</li>
<li>
<a href="#">Sub-Item 4</a>
</li>
<li>
<a href="#">Sub-Item 5</a>
</li>
</ul>
</div>
</div>
</li>
<li class="top">
<a href="#">
<strong>About</strong>
</a>
<div class="sub">
<div>
<ul>
<li>
<a href="#">One Sub-Item</a>
</li>
<li>
<a href="#">Another Sub-Item</a>
</li>
<li>
<a href="#">Still a Sub-Item</a>
</li>
</ul>
</div>
</div>
</li>
<li>
<a href="#">
<strong>Contact</strong>
</a>
</li>
</ul>
</div>
Wie gewohnt eine ungeordnete Liste für die Haupt-Navigation und jeweils eine für die Sub-Navigationen. Um die charakteristischen Ecken hinzubekommen, müssen zwei Elemente ineinander verschachtelt und gegeneinander verschoben werden. Das macht den Quelltext Tag-intensiver. In meinen Augen jedoch noch in einem vertretbaren Rahmen und weit entfernt von klassischer "Diveritis".
Als nächstes kommen wir zum CSS:
CSS
@font-face {
font-family: 'SilkscreenNormal';
src: url('slkscr-webfont.eot');
src: local('☺'), url('slkscr-webfont.woff') format('woff'), url('slkscr-webfont.ttf') format('truetype'), url('slkscr-webfont.svg#webfontUx1SMfhe') format('svg');
font-weight: normal;
font-style: normal;
}
ul {
list-style: none;
}
body {
background-color: #FFF;
color: #333;
font: normal 13px SilkscreenNormal, sans-serif;
}
#nav,
#nav > ul,
#nav > ul > li {
float: left;
display: inline;
}
#nav,
#nav > ul {
width: auto;
_width: 1%; /* IE6 Hack */
height: 32px;
}
#nav {
margin: 50px;
position: relative;
border-width: 2px 0;
border-style: solid;
border-color: #666;
}
#nav > ul {
position: relative;
left: -2px;
margin-right: -4px;
padding: 0 10px;
border-width: 0 2px;
border-style: solid;
border-color: #666;
background: url('data:image/gif;base64,R0lGODlhBAAEAIAAAP///73n/yH5BAAAAAAALAAAAAAEAAQAAAIGTACGqBkFADs=') 0 0 repeat;
*background: url('img/tile1.gif') 0 0 repeat; /* IE6 and IE7 can't handle data uris */
}
#nav > ul > li {
_width: 1%; /* IE6 Hack */
margin: 3px 5px;
position: relative;
}
#nav > ul > li > a {
display: block;
position: relative;
width: auto;
height: 22px;
border-width: 2px 0;
border-style: solid;
border-color: #999;
background-color: #FFF;
}
#nav > ul > li > a:link,
#nav > ul > li > a:visited {
color: #999;
text-decoration: none;
background: url('data:image/gif;base64,R0lGODlhBAAEAIAAAP/////fvSH5BAAAAAAALAAAAAAEAAQAAAIGTACGqBkFADs=') 0 0 repeat;
*background: url('img/tile2.gif') 0 0 repeat; /* IE6 and IE7 can't handle data uris */
}
#nav > ul > li > a:hover,
#nav > ul > li > a:focus,
#nav > ul > li > a:active {
color: #666;
background-image: url('data:image/gif;base64,R0lGODlhBAAEAIAAAP/////MmSH5BAAAAAAALAAAAAAEAAQAAAIGTACGqBkFADs=');
*background-image: url('img/tile3.gif'); /* IE6 and IE7 can't handle data uris */
}
#nav > ul > li > a strong {
display: block;
position: relative;
width: auto;
height: 22px;
padding: 0 10px;
line-height: 22px;
left: -2px;
margin-right: -4px;
border-width: 0 2px;
border-style: solid;
border-color: #999;
}
#nav > ul > li.top > a > strong {
padding-left: 21px;
background: url('data:image/gif;base64,R0lGODlhBgAEAIABAJmZmf///yH5BAEAAAEALAAAAAAGAAQAAAIHhI8WocuwCgA7') 5px center no-repeat;
*background: url('img/arrow.gif') 5px center no-repeat; /* IE6 and IE7 can't handle data uris */
}
#nav > ul > li > a:hover,
#nav > ul > li > a:hover strong,
#nav > ul > li > a:focus,
#nav > ul > li > a:focus strong {
border-color: #666;
}
#nav > ul > li.top > a:hover strong,
#nav > ul > li.top > a:focus strong {
background-image: url('data:image/gif;base64,R0lGODlhBgAEAIABAGZmZv///yH5BAEAAAEALAAAAAAGAAQAAAIHhI8WocuwCgA7');
*background-image: url('img/arrow_hv.gif'); /* IE6 and IE7 can't handle data uris */
}
#nav > ul > li > a:active {
top: 1px;
}
.sub {
position: absolute;
width: auto;
top: 24px;
left: 0;
padding-top: 9px;
display: none;
}
.sub div {
position: relative;
border-top: 2px solid #666;
border-bottom: 2px solid #666;
}
.sub div ul {
position: relative;
left: -2px;
margin-right: -4px;
border-left: 2px solid #666;
border-right: 2px solid #666;
background: url('data:image/gif;base64,R0lGODlhBAAEAIAAAP///+7u7iH5BAAAAAAALAAAAAAEAAQAAAIGTACGqBkFADs=') 0 0 repeat;
*background: url('img/tile4.gif') 0 0 repeat; /* IE6 and IE7 can't handle data uris */
}
.sub div ul li {
border-top: 2px solid #666;
}
.sub div ul li:first-child {
border-top: none;
}
.sub div ul li a {
display: block;
padding: 0 10px;
line-height: 22px;
font-size: 12px;
white-space: nowrap;
}
.sub div ul li a:link,
.sub div ul li a:visited {
color: #666;
}
.sub div ul li a:hover,´
.sub div ul li a:focus {
background: url('data:image/gif;base64,R0lGODlhBAAEAIAAAP///93d3SH5BAAAAAAALAAAAAAEAAQAAAIGTACGqBkFADs=') 0 0 repeat;
*background: url('img/tile5.gif') 0 0 repeat; /* IE6 and IE7 can't handle data uris */
}
.sub div ul li a:active {
background: url('data:image/gif;base64,R0lGODlhBAAEAIAAAP///8zMzCH5BAAAAAAALAAAAAAEAAQAAAIGTACGqBkFADs=') 0 0 repeat;
*background: url('img/tile6.gif') 0 0 repeat; /* IE6 and IE7 can't handle data uris */
}
Als erstes binde ich den Pixel-Font "SilkScreen von Jason Kottke ein. Danach folgen die Angaben für die Navigation.
Interessant ist dabei, dass selbst das äußere Element <div id="nav"> die Angabe float: left hat, damit es sich der Breite des Inhalts anpasst. Das kann zu Layout-Problemen führen, weshalb man im praktischen Einsatz darauf achten muss, das Element direkt unterhalb der Navigation mit einem clear: left zu versehen.
Des weiteren kann man sehen, wie die charakteristischen Ecken zustande kommen: Das äußere Element hat jeweils unten und oben eine zwei-Pixel-starke border und das innere jeweils links und rechts. Das innere Element wird dann per left: -2px und margin-righ: -4px um jeweils zwei Pixel nach links und rechts aus dem umgebenden Element hinaus gezogen. Schon ist der gewünschte Effekt da.
Ebenfalls erwähnenswert sind die Grafiken. Da diese nur 4x4 Pixel bzw. 4x6 Pixel groß sind, lohnt es sich, sie in Form von Data URIs einzubinden und so unnötige HTTP Requests zu sparen. Blöderweise können IE6 und IE7 damit nicht umgehen, weshalb die richtigen Grafiken ebenfalls eingebunden werden müssen. Diese werden dann mithilfe des Star-Hack den beiden Browsern zugewiesen.
Bilder in Data URIs umwandeln könnt ihr übrigens mit diesem Online-Tool.
Zum Schluss noch etwas jQuery um die Fly-Out-Menu-Funktionalität zu realisieren:
JavaScript
$(document).ready( function() {
$('#nav li.top').hover( function() {
$(this).find('div').stop(true, true).fadeIn('slow');
}, function() {
$(this).find('div').stop(true, true).fadeOut('slow');
});
});
Ich denke, das ist die spartanischste Lösung und bedarf keiner weiteren Erläuterung.
Das war es auch schon. Die Navigation ist beliebig per Copy&Paste erweiterbar, denkt nur daran, den li-Elementen, die eine Sub-Navigation beinhalten, die Klasse top zu verpassen, damit die Fly-Out-Menu-Funktionalität gewährleistet ist.
Ansonsten wünsche ich viel Spaß mit der Navigation. Bei Fragen bitte wie immer die Kommentar-Funktion nutzen. Und bei Gefallen fleißig via Twitter und Facebook verbreiten. Vielen Dank
“Rising Curtain”-Effekt bei Input-Feldern mit jQuery

Hinweis:
Die CSS-Eigenschaft background-position-x bzw. background-position-y ist eine Erfindung von Microsoft und funktioniert leider nur im Internet Explorer und in Webkit-Browsern. Deshalb gibt es im jQuery-Skript eine Browser-Weiche. Im Firefox und im Opera verschwindet die Grafik einfach nur, der "Rising Curtain"-Effekt findet also nicht statt.
Kommen wir aber zum eigentlichen Tutorial:
Heute möchte ich zeigen, wie man input-Felder in Formularen mithilfe des "Rising Curtain"-Effekts und jQuery interessanter gestalten kann, wenn der Fokus auf ihnen liegt. Alles was man dafür braucht, ist eine kleine Hintergrundgrafik mit einem Farbverlauf und ein paar Zeilen jQuery. Als erstes aber wenden wir uns dem HTML-Teil zu:
HTML
<div>
<span>
<input type="text" id="input1" />
</span>
<label for="input1">Input 1</label>
</div>
Dieser bedarf wohl keiner großen Erklärung. Es gibt ein input-Feld plus dem dazugehörigen Label. Das input-Feld ist in span-Tags eingefasst, welche für den Rand sorgen.
Als nächstes der CSS-Teil:
CSS
div {
height: 24px;
margin-bottom: 10px;
clear: left;
}
span {
height: 24px;
float: left;
display: inline;
margin-right: 10px;
border: 1px solid #999;
}
span.active {
border-color: #063050;
}
input {
display: block;
width: 200px;
height: 18px;
padding: 2px 4px;
line-height: 18px;
background: #FFF url('img/gradient.jpg') 0 0 repeat-x;
border: 1px solid #FFF;
color: #063050;
}
label {
height: 24px;
float: left;
display: inline;
line-height: 24px;
cursor: pointer;
}
span und label werden mit float: left nebeneinander angeordnet. Das input-Feld bekommt einen weißen Rand, damit der dunkle Hintergrund nicht an den dunklen Rand des span-Elements klatscht. Der Hintergrund wiederum stellt einen Farbverlauf von dunkelblau zu weiß dar, der mehr als doppelt so hoch wie das input-Feld ist.
Kommen wir nun zum jQuery-Teil:
JavaScript
jQuery(document).ready(function($) {
$('input').focus( function() {
$(this).parent().addClass('active');
if ( jQuery.browser.webkit || jQuery.browser.msie ) {
$(this).animate({'background-position-y' : '-28px'}, 500, 'linear');
} else {
$(this).animate({'background-position' : '0 -28px'}, 0, 'linear');
}
});
$('input').blur( function() {
$(this).parent().removeClass('active');
if ( jQuery.browser.webkit || jQuery.browser.msie ) {
$(this).animate({'background-position-y' : '0'}, 500, 'linear');
} else {
$(this).animate({'background-position' : '0 0'}, 0, 'linear');
}
});
});
Liegt der Fokus auf einem input-Feld, bekommt das umschließende span-Element die Klasse "active" zugewiesen und der Rand wird dunkelblau. Außerdem verschiebt sich der Hintergrund des input-Feldes nach oben mithilfe der animate()-Funktion von jQuery. Das ist der so genannte "Rising Curtain"-Effekt, durch den der Hintergrund des input-Feldes heller wird. Gesteuert wird das ganze über die CSS-Eigenschaft background-position-y.
So einfach ist das. Das ganze lässt sich natürlich auch bei einer textarea anwenden. Denkt aber daran, dass die Hintergrund-Grafik entsprechend groß sein muss. Auch ist es denkbar, die Hintergrund-Grafik ganz aus dem sichtbaren Bereich fahren zu lassen, so dass das input-Feld weiß wird. Der Kreativität sind kaum Grenzen gesetzt.
Das war es auch schon. Bookmark setzen oder dieses Tutorial via twitter durch den Äther jagen, wird wie immer gern gesehen. Bei Fragen und Anregungen steht die Kommentar-Funktion zur Verfügung. Nichts neues also.
7 hilfreiche Tutorials zum Thema Transitions in CSS3
Das Thema Transition in CSS3 ist so neu nicht mehr, doch nach wie vor aktuell. Das liegt u.a. daran, dass mit Firefox und Chrome neuerdings zwei weitere Browser neben Safari auf dem Markt sind, die diese Funktion unterstützen.
So entwickeln sich CSS-Transitions von der Spielerei zur ernsten Alternative zu JavaScript, wenn es um Animationen im Webdesign geht. Deshalb habe ich 7 Tutorials rausgesucht, die einen Überblick über die Möglichkeiten von CSS-Transitions geben.
1. CSS: Transition Timing Functions

Der Artikel behandelt die Eigenschaften transition-duration und transition-timing-function, und beschreibt, wie man Dauer und Geschwindigkeit von Animationen beeinflusst.
2. CSS Transforms and Transitions - The OSX Dock example

Chris Walker zeigt, wie man den Mac OSX-Dock nur mithilfe von CSS3 nachbauen kann.
3. Going Nuts with CSS Transitions

Natalie Downe demonstriert, wie man eine Polaroid-Galerie mithilfe von CSS-Transitions baut.
4. Nicer Navigation with CSS Transitions

Schöne Anleitung, wie man Website-Navigationen schöner gestalten kann mit CSS3.
5. Create the accordion effect using CSS3

Der Akkordeon-Effekt nur mit CSS3 und Transitions realisiert.
6. CSS3 Hover Tabs ohne JavaScript

Hover Tabs ohne JavaScript. Über die Praxistauglichkeit dieser Lösung kann man streiten, aber nichtsdestotrotz veranschaulicht sie sehr schön die Möglichkeiten von CSS3.
7. CSS Transitions 101

Zum Abschluss eine schöne Gesamtübersicht zum Thema CSS-Transitions auf Webdesigner Depot.
Ich hoffe, ihr habt ein paar hilfreiche Sachen gefunden. Zwar surfen viele Leute noch immer mit einem veralteten Browser, der keine CSS-Transitions unterstützt, doch trotzdem gibt es Möglichkeiten, diese Technik bereits jetzt einzusetzen. Es sind eben die Details, wenn bspw. ein Link nicht schlagartig beim Hover die Farbe ändert, sondern das langsam tut, die eine moderne Website ausmachen. Hat man keinen modernen Browser, kommt man nicht in den Genuss, kann die Seite aber trotzdem nutzen.
Hover- und Spotlight-Effekt in Bildergalerie mit jQuery

Jede Bildergalerie gewinnt, wenn das Thumbnail-Bild unter dem Cursor durch einen Hover-Effekt hervorgehoben wird und der Nutzer weiß, was gerade Sache ist. Wenn die umliegenden Thumbnails dann auch noch durch einen Spotlight-Effekt abgedunkelt werden, ist es noch leichter für den Nutzer, sich auf das aktuelle Bild zu konzentrieren. Wie man das mit jQuery realisiert, zeige ich jetzt.
Als erstes der HTML-Teil:
HTML
<body class="js">
<div class="wrap">
<a href="path/to/image.html" class="image">
<img src="path/to/image.jpg" alt="" />
</a>
<a href="path/to/image.html" class="image">
<img src="path/to/image.jpg" alt="" />
</a>
<a href="path/to/image.html" class="image">
<img src="path/to/image.jpg" alt="" />
</a>
</div>
</body>
Als nächstes der CSS-Teil:
CSS
.wrap {
width: 321px;
height: 321px;
padding: 20px 0 0 20px;
margin: 30px auto;
clear: left;
}
.image {
width: 77px;
height: 77px;
padding: 5px;
margin: 0 20px 20px 0;
float: left;
display: inline;
position: relative;
z-index: 10;
background-color: #CCC;
}
.js .image:hover {
background-color: #36F;
}
.image img {
position: relative;
z-index: 30;
border-width: 1px;
border-style: solid;
border-color: #666 #999 #999 #666;
}
.layer1, .layer2 {
display: block;
position: absolute;
}
.layer1 {
width: 87px;
height: 87px;
top: 0;
left: 0;
z-index: 20;
background-color: #36F;
}
.layer2 {
width: 77px;
height: 77px;
top: 5px;
left: 5px;
z-index: 40;
background-color: #000;
}
Kleine Anmerkung dazu: Wenn JavaScript im Browser aktiviert ist, wird die Klasse js vom body-Tag entfernt. Geschieht dies nicht, bekommt das a-Tag um die Thumbnails einen :hover-Effekt.
Schlussendlich der jQuery-Teil:
JavaScript
jQuery(document).ready(function($) {
$('body').removeClass('js');
var image = $('a.image');
var html = '<span class=\"layer1\"></span><span class=\"layer2\"></span>';
image.append(html).find('span').css('opacity', '0');
image.each( function() {
$(this).hover( function() {
$('span.layer1', this).stop().fadeTo(800, 1);
$(this).siblings().find('span.layer2').stop().fadeTo(800, 0.5);
}, function() {
$('span.layer1', this).stop().fadeTo(800, 0);
$(this).siblings().find('span.layer2').stop().fadeTo(800, 0);
});
});
});
Als erstes wird die Klasse js vom body-Tag entfernt. Danach werden die beiden span-Tags eingefügt, die für den Hover- und den Spotlight-Effekt zuständig sind. Danach wird in einer each()-Schleife festgelegt, dass bei einem Hover die span-Tags erscheinen und entsprechend wieder verschwinden.
Das war es auch schon. Falls es Fragen oder Probleme gibt, möchte ich euch wie immer die Kommentar-Funktion nahe legen. Ansonsten freue ich mich natürlich, wenn ihr den Artikel bookmark't oder auf twitter verbreitet.
Animierte Image-Caption mit jQuery unter WordPress

Google liebt es ja, wenn unter oder neben Bildern noch ein beschreibender Text steht. Deswegen hat WordPress vor einiger Zeit die Image-Caption eingeführt. Diese kann man - wenn man kein Freund von Bildunterschriften ist - mit jQuery jedoch so manipulieren, dass sie nur bei einem MouseOver erscheint. Wie das geht, erkläre ich in diesem Tutorial.
Wie gehabt als erstes der HTML-Teil:
HTML
<div class="wp-caption" style="width: ???px">
<img src="/path/to/img.jpg" alt="Dies ist eine Beschreibung" title="Dies ist eine Beschreibung" width="???" height="???" class="size-full wp-image-3402" />
<p class="wp-caption-text">Dies ist eine Beschreibung</p>
</div>
Dies ist der Caption-Quelltext, der von WordPress ausgegeben wird. Anstatt der Fragezeichen kommen natürlich die richtigen Höhen- und Breitenangaben, ebenso wie der richtige Pfad zum Bild.
Als nächstes ein bißchen CSS für das entsprechende Aussehen:
CSS
.wp-caption {
position: relative;
margin-bottom: 20px;
border: 3px solid #999;
overflow: hidden;
-moz-border-radius: 2px;
-khtml-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
}
.wp-caption-text {
padding: 5px 10px;
background-color: #333;
color: #EEE;
border-top: 1px solid #666;
}
Wichtig hierbei ist die Angabe "position: relative", da wir beim Verschieben des Bildes mit einem negativen margin arbeiten und der IE6 ohne diesen Zusatz rumzickt. Des weiteren ist die Angabe "overflow: hidden" wichtig, da der Caption-Text sonst nicht verschwinden würde.
Alle anderen Angaben zu Farben, Innen- und Außenabständen können ohne weiteres angepasst werden.
Zum Schluss den nötigen JavaScript- bzw. jQuery-Teil, ohne den hier gar nichts läuft:
HTML
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
</head>
Im Kopf-Bereich binden wir die jQuery-Library ein.
JavaScript
jQuery(document).ready(function($) {
$('div.wp-caption').each(function(i) {
var img_ = $('img', this);
var img_height = img_.attr('height');
var p_height = $('p', this).outerHeight();
$(this).height(img_height);
$(this).hover(function() {
img_.animate({marginTop : -p_height}, 500);
}, function() {
img_.animate({marginTop : '0'}, 500);
});
});
});
Mit der each()-Funktion wird jede einzelne Image-Caption auf der Seite angesprochen. Danach wird die Höhe des dazugehörigen Bildes und der Beschreibung gespeichert. Als nächstes wird die Höhe des Caption-div gleich der Höhe des Bildes gesetzt, so dass die Beschreibung abgeschnitten und nicht sichtbar ist.
Daraufhin wird eine hover()-Funktion aufgemacht. Fährt man mit dem Cursor über eine Image-Caption, bekommt das dazugehörige Bild einen negativen margin-top, der der Höhe der Beschreibung entspricht, so dass diese in den sichtbaren Bereich fährt. Verlässt man die Image-Caption wieder mit dem Cursor, wird der margin-top des Bildes wieder auf Null gesetzt. Das ganze wird mit der animate()-Funktion von jQuery animiert.
Das war es auch schon, fertig ist die animierte Image-Caption unter WordPress. Wobei natürlich nichts dagegen spricht, das ganze außerhalb von WordPress einzusetzen. Wie immer verweise ich an die Kommentar-Funktion, falls es Fragen gibt oder etwas nicht so funktioniert, wie es soll.
Schwarzweiß-Foto mit jQuery in Farb-Foto verwandeln, wenn man mit dem Cursor drüberfährt
Wie euch vielleicht aufgefallen ist, habe ich in die Sidebar eine kurze Autoren-Info gepackt, damit die Leute wissen, wer hier schreibt. Und wer von euch mal mit dem Cursor über das Foto drüber gefahren ist, wird gemerkt haben, dass sich das Schwarzweiß-Foto dabei in ein Farb-Foto verwandelt. Das ist sicher kein Basis-Feature, aber eine nette jQuery-Spielerei. Und wie das geht, verrate ich jetzt.
Als erstes brauchen wir den entsprechenden HTML-Code:
HTML
<div id="image">
<img src="image_bw.jpg" alt="Scharzweiß-Bild" />
</div>
Ein normales, von einem div-Element umschlossenes Bild. Im img-Element ist das Schwarzweiß-Foto eingebunden.
Als nächstes etwas CSS:
CSS
#image {
width: …;
height: …;
background: url('image_color.jpg') 0 0 no-repeat;
}
Die Breite und Höhe des div-Elements entsprechen der Breite und Höhe eures Bildes. Als Hintergrund-Bild ist das Farb-Foto eingebunden. Es befindet sich also die ganze Zeit hinter dem Schwarzweiß-Foto und wird von diesem verdeckt.
Nun zum jQuery-Teil:
JavaScript
$(document).ready(function() {
$('#image img').hover(function() {
$(this).stop().fadeTo('slow', 0);
}, function() {
$(this).stop().fadeTo('slow', 1);
});
});
Fährt man mit dem Cursor über das Schwarzweiß-Foto, wird dieses ausgeblendet und offenbart das Farb-Foto. Geht man mit dem Cursor vom Foto runter, wird das Schwarzweiß-Foto wieder eingeblendet. Das ganze mit der Geschwindigkeitsangabe slow, damit es auch schön geschmeidig vonstatten geht.
Das war es auch schon. Bei Fragen oder Anregungen sei wie immer auf die Kommentar-Funktion verwiesen.
PS: Eine Demo des ganzen gibt es auf der Blog-Startseite!
Der "perfekte" WordPress-Suchschlitz

Heute zeige ich, wie man mit etwas JavaScript und PHP einen benutzerfreundlichen Suchschlitz in WordPress baut. Das "perfekt" steht deshalb in Anführungszeichen, weil ich mir nicht anmaßen möchte, den wirklich perfekten Suchschlitz zu präsentieren. Möglicherweise würde es "advanced" eher treffen, aber wie klingt das denn bitte?!
Also los: Wir wollen einen Suchschlitz, in dem "Suchen…" steht, was wiederum verschwinden soll, wenn der Nutzer auf das Eingabefeld klickt und wieder erscheinen soll, wenn der Nutzer außerhalb des Eingabefeldes klickt. Des weiteren soll auf der Suchergebnis-Seite im Suchschlitz der Suchbegriff stehen, welcher ebenfalls verschwinden und erscheinen soll, je nachdem, wohin der Nutzer klickt.
Das klingt extrem kompliziert, ich weiß, aber bei Verständnisproblemen einfach die Demo anschauen. Ansonsten lege ich mal los. Und zwar brauchen wir folgenden HTML-Teil in der WordPress-Theme-Datei "searchform.php":
HTML/PHP
<form method="get" id="search" action="<?php bloginfo('url'); ?>/">
<fieldset>
<input type="text" value="<?php echo $search_text; ?>" id="s" name="s" />
<div>
<input type="submit" name="submit" value="Absenden" />
</div>
</fieldset>
</form>
Falls ihr in eurem WordPress-Theme keine "searchform.php" habt, legt sie einfach an, ihr könnt sie mit folgendem Code-Schnippel in eure Templates einbinden:
PHP
<?php include (TEMPLATEPATH . "/searchform.php"); ?>
Ansonsten handelt es sich um ein simples Formular mit einem Eingabefeld und einem Submit-Button, welchen ich in ein div-Element eingebettet habe. Warum, könnt ihr in meinem Beitrag zum "Barrierefreien Button mit Text Replacement" nachlesen.
Als nächstes kommt der JavaScript-Teil, der dafür sorgt, dass das Eingabefeld beim Draufklicken geleert wird und zusätzlich dem Submit-Button die Klasse submit_active anhängt, wodurch der Farbwechsel im Schein um den Suchschlitz realisiert wird.
JavaScript
window.onload = function() {
var input = document.getElementById('s');
var value = document.getElementById('s').value;
var submit = document.getElementsByName('submit')[0];
mFocus = function() {
if ( this.value == value ) {
this.value = '';
}
submit.className = 'submit_active';
}
mBlur = function() {
if ( this.value == '' ) {
this.value = value;
}
submit.className = '';
}
with ( input ) {
onfocus = mFocus;
onblur = mBlur;
}
}
Als erstes werden die Selektoren für das Eingabefeld, für dessen Value und den Submit-Button in Variablen gespeichert. Danach die Funktionen definiert, die den Value wechseln und die Klasse submit_active anhängen bzw. entfernen. Zum Schluss werden diese den Events onfocus und onblur zugewiesen.
Als nächstes brauchen wir ein wenig PHP, um auf der Suchergebnis-Seite den spezifischen Suchbegriff im Suchschlitz erscheinen zu lassen:
PHP
<?php
if (!is_search()) {
$search_text = "Suche…";
} else {
$search_text = "$s";
}
?>
Über if (!is_search()) fragen wir ab, ob es sich nicht um die Suchergebnis-Seite handelt (man beachte das Ausrufezeichen, welches die Bedingung negiert). Ist dem so, speichert die Variable $search_text den Wert "Suche…". Andernfalls, wenn der Besucher sich auf der Suchergebnis-Seite befindet, speichert die Variable den spezifischen Suchbegriff. Wenn wir nochmal hoch zum HTML-Teil gucken, können wir sehen, dass die Beschriftung des Suchschlitzeingabefeldes mit genau dieser Variable $search_text realisiert wird, indem das Attribut value den Wert <?php echo $search_text; ?> bekommt.
Zum Schluss muss der ganze Spaß nur noch gestylet werden:
CSS
#search {
width: 301px;
height: 52px;
clear: left;
}
#s,
#search div input,
#search div input.submit_active {
background-image: url('images/sprite.jpg');
background-repeat: no-repeat;
}
#s {
width: 231px;
height: 22px;
padding: 17px 12px 13px 17px;
float: left;
border: none;
line-height: 22px;
font-size: 16px;
color: #666;
background-position: 0 0;
}
#search div {
width: 41px;
height: 52px;
float: left;
overflow: hidden;
}
#search div input {
width: 41px;
padding-top: 52px;
border: none;
background-position: -260px 0;
cursor: pointer;
}
#s:focus {
background-position: 0 -52px;
color: #369;
}
#search div input.submit_active {
background-position: -260px -52px;
}
#search div input:hover {
background-position: -301px -0;
}
#search div input.submit_active:hover {
background-position: -301px -52px;
}
Ich denke, die Style-Angaben sind relativ selbsterklärend. Die Input-Elemente werden formatiert und die jeweiligen Zustände mit der entsprechenden Hintergrundgrafik versehen, wobei ich hier auf ein sog. CSS-Sprite zurückgreife, wodurch sich die Grafik-Datei nicht ändert, sondern nur deren Position. Außerdem nutze ich immer ein sog. CSS-Reset, wodurch u.a. das Element fieldset direkt am Anfang die Style-Angabe border: 0 zugewiesen bekommt, weshalb das hier im CSS-Teil nicht auftaucht. Bedenkt das, wenn ihr die Sachen hier direkt aus dem Beitrag rauskopiert. Im Zweifelsfalle geht ihr auf Nummer sicher, wenn ihr die Beispiel-Dateien runterladet.
Außerdem dürften der IE6 und der IE7 Probleme mit der Darstellung haben, da sie nicht mit den Pseudo-Klassen :hover und :focus um können, bzw. erstere nur bei Links anwenden können. Um diesen Makel zu beheben, guckt nochmal in meinen Beitrag zum Thema Internet Explorer 6.
Ansonsten ist der "perfekte" Suchschlitz fertig. Ich habe noch die .psd-Datei für das CSS-Sprite in den Download-Ordner gepackt, damit ihr die Farben bei Bedarf anpassen könnt. Alternativ könnt ihr natürlich auch ein ganz eigenes Design machen.
In diesem Sinne viel Spaß damit und bei Fragen immer ran an die Kommentar-Funktion.
Nachtrag:
Ich habe im JavaScript-Teil zwei Ergänzungen bei der mFocus- und der mBlur-Funktion vorgenommen. Bevor der Inhalt des Eingabefeldes automatisch gelöscht oder mit dem Wert "Suche…" aufgefüllt wird, findet eine Abfrage statt. So wird verhindert, dass das vom Nutzer eingegebene Suchwort nach dem Klicken außerhalb des Eingabefeldes verschwindet.
