Dieser Artikel soll zeigen, wie man sein Suckerfish wesentlich gebrauchstauglicher und barrierefreier (sowohl BITV- als auch WAI-kompatibel) machen kann.
Langatmige Erklärung überspringen - Direkt zur Einbauanleitung, dem Script-Download und der Demo.
Das Suckerfish Dropdown Menue gehört unter Webentwicklern zu den beliebtesten und besten dynamischen Ausklappmenü´s/Pulloutmenü´s. Dennoch wird es (wie auch alle anderen) in der Regel nicht eingesetzt, wenn es darum geht, eine barrierefreie Seite zu realisieren. Die Gründe dafür sind u.a., dass die Unterkategorien einerseits für Tastatur- und Screenreadernutzer entweder nicht zugänglich sind oder zu viele unsichtbar bleibende Untermenuepunkte durchgeskippt werden müssen andererseits die Mausnutzung gerade bei horizontalen Flyouts etwas hackelig werden kann (man bedenke zusätzlich die Nutzer von "Mausersatzprodukten").
Um nicht das Rad neu zu erfinden, stützt sich der Artikel bzgl. der CSS-Grundlagen auf den Code Drop-Down Menus, Horizontal Style. Es werden nur die wesentlichen Änderungen hier erwähnt. Vom eigentlichem Suckerfish-JavaScript wird am Ende dagegen nichts mehr übrig bleiben.
In der Original-Variante des Suckerfish´s werden die Untermenue-Punkte mit Hilfe von display:none versteckt. Dies behalten wir grundsätzlich bei, verschieben das Untermenue jedoch noch zusätzlich aus dem Viewport. Für die aktive Kategorie heben wir sodann display: none wieder auf.
#nav li ul {
position: absolute;
left: -999px;
display: none;
}
#nav li.active ul,
#nav li.activepath ul {
display: block;
}
Warum nur für die aktive Kategorie? Zu viele Links sind bei Tastatur- und vor allem bei Screenreadernutzung störend.
Um nun die fokusierten Untermenuelinks auch sichtbar zu machen, fügen wir folgenden Stil hinzu:
#nav a:focus+ul,
#nav a:active+ul {
left: 149px; /* 1px weniger als Navigationsbreite */
}
#nav li ul li a:focus,
#nav li ul li a:active {
position: absolute;
left: 1148px; /* Navigationsbreite + Untermenueverschiebung - 1px */
}
Damit die Links bei Fokusierung durch die Maus nicht verschoben werden, heben wir die oben gemachte Stilanweisung für focus und active bei zusätzlichem hover wieder auf:
#nav li ul li a:hover,
#nav li ul li a:hover,
#nav li:hover ul li a:focus,
#nav li:hover ul li a:active {
position: static;
}
Um die Mausnutzung zu vereinfachen, kann man nun mit Hilfe von JavaScript eine kurze Verzögerung / Trägheit einbauen, welche dazu führt, dass das Untermenue nicht sofort verschwindet, sobald man das Untermenue mit der Maus verlässt. Eine solche Verzögerung entspricht dem bekannten und teilweise auch erwarteten Verhalten von Flyout Menues. Zusätzlich lässt sich die Optik bei Tastaturnutzung verbessern.
Das JavaScript könnte beispielsweise so aussehen (erfordert JQuery):
//Verzögerung in ms
var _DF_navtimer = "360";
//Konfig Ende
var _DF_NavTimeID;
/**
* @param {String} $selektor CSS-Selektor des Elements, welches die ungeordnete Navigationsliste umgibt
* @param {String} $navlistType optionaler Listentype z.B. ol (default = ul)
*/
function DickerFisch($selektor,$navlistType) {
var $navlistType = $navlistType || 'ul';
_DF_obj = this;
var _waitingnavclass = 'DF_waitlis', _activnavclass = 'DF_activelis';
_DF_obj.$navstatus = function(_eventobj,$action){
_DF_obj._eventobj = _eventobj;
switch($action){
case "hide":
$($selektor+' li.'+_waitingnavclass).removeClass(_waitingnavclass);
if(_eventobj.className.indexOf(_activnavclass) != -1){
_DF_NavTimeID = window.setTimeout('_DF_obj._hide(_DF_obj._eventobj)',_DF_navtimer);
}
break;
case "show":
if(typeof $($selektor+' li.'+_activnavclass).get(0) == "undefined" || _eventobj.className.indexOf(_activnavclass) != "-1") {
_DF_obj.$show(_eventobj);
$(_eventobj).addClass(_activnavclass);
window.clearTimeout(_DF_NavTimeID);
} else {
$(_eventobj).addClass(_waitingnavclass);
}
break;
};
};
_DF_obj.$show = function(_eventobj){
$(_eventobj).addClass('over').ancestors("li").addClass('over');
};
_DF_obj._hide = function(_eventobj){
$($selektor+' li.over').removeClass('over');
$($selektor+' li.'+_activnavclass).removeClass(_activnavclass);
if(_eventobj.className.indexOf(_waitingnavclass) != -1) {
_DF_obj.$navstatus($($selektor+' li.'+_waitingnavclass).get(0),"show");
}
};
//Init
$($selektor+" li").filter("["+$navlistType+"]").each(function(i){
$(this).hover( function() {
_DF_obj.$navstatus(this,'show');
}, function() {
_DF_obj.$navstatus(this,'hide');
} );
});
$($selektor+" li "+$navlistType+" a").focus( function() {
$(this).ancestors("li").addClass('navfocus');
} );
$($selektor+" li "+$navlistType+" a").blur( function() {
$(this).ancestors("li").removeClass('navfocus');
} );
$($selektor+">"+$navlistType).addClass("navfxenabled");
};
Eine, in diesem Zusammenhang, erwähnenswerte Technik ist das ultimative css [only] drop-down-menu von Stu Nicholls. An diesem könnten Puristen bemängeln, dass es innerhalb der konditionellen Kommentare Layouttabellen sowie unzulässige Codeverschachtelungen verwendet, um dem IE ohne JavaScript das "Fliegen" beizubringen. Angesichts der Tatsache, dass sich dieser Teil des Codes formal in einem Kommentar befindet und nicht jeder html-Fehler und nicht jede Layout-Tabelle eine Barriere darstellt, ist dieses Technik als sauber zu bezeichnen, sofern sie im IE zu einer vergleichbaren Ausgabe führt, wie in den standardkonform rendernden Browsern. Leider tut diese Technik dies gerade eben nicht.
Da der Link der Oberkategorien alle darauffolgenden Links der Unterkategorien mit einschliesst, ist es beispielsweise nicht mehr möglich die Links durch ein (verstecktes) Zeichen zu trennen (dies ist für Screenreader wie den IBM Homepage Reader, interessant, der Links durch einen Stimmwechsel kennzeichnet und auf den IE 6 aufbaut).
Aber auch Screenreader, die jeden Link extra angeben, sind mit dieser Technik überfordert, sofern der IE6- zum Surfen verwendet wird.
So liest beispielsweise der in Deutschland sehr beliebte Screenreader, Virgo, den ersten Link komplett (also mit allen Unterkategorien vor).
Das beigefügte Bild zeigt die Webformator-Ausgabe (u.a. Virgo + IE6) am Beispiel des HTML-Codes von Stu Nicholls, wobei die inhaltlich nicht vorzulesenden Teile rot unterstrichen wurden. (Übrigens ist auch sehr schön zu sehen, wie sich teilweise unnötige title-Werte negativ auswirken.)
Damit verbleibt nur die Möglichkeit dem IE6- bei ausgeschaltetem Javascript entweder ein alternatives Stylesheet oder eine alternative Unterkategorie zukommen zu lassen (NOSCRIPT ist noch lange nicht tot!).
<!--[if lte IE 6]><noscript>
Inhalt/Stil für die Unfähigen
</noscript><![endif]-->
Das DHTML-Menue unterstützt neben der normalen Tastaturnutzung TAB / Shift + TAB (bei Opera u.a.: A / Q) auch Opera´s Spatial Navigation Shift + Pfeiltasten. Wie gut dies letztendlich funktioniert, hängt allerdings stark vom Design und der Designumsetzung ab, da Opera Probleme bei nahe beieinander liegenden Links - wie sie bei einem dynamischen Menue zwangsläufig auftreten - mit der Link-Auswahl hat.
Beispiel: Das CSS aus dem oben genannten Artikel »Drop-Down Menus, Horizontal Style« erstellt den Rahmen um die Navigation dadurch, dass es den Links eine border zuweisst. Damit sich diese border bei ausgeklapptem Menü nicht verdoppeln, wird die Unternavigation um einen Pixel nach links versetzt. Hierdurch überschneiden sich die Links und Opera kann den nächsten Link des Untermenüs nicht mehr sauber auswählen. Das hier verwendete abgewandelte CSS erzeugt dagegen den Rahmen, in dem es dem umgebenden li-Element eine Border zuweißt, auf diese Weise kommt es zu keiner Überschneidung mehr.
Sollte euer Drop Down Menü Opera´s Spatial Navigation schlecht bzw. überhaupt nicht unterstützen, solltet ihr einen Blick in das JavaScript werfen:
var _limitlinkforkeyboard = 2;
Das Script erwartet als (X)HTML-Struktur eine valide verschachtelte (ungeordnete, geordnete etc.) Liste, welche von einem weiteren Element (z.B. div-Container mit id="nav") umschlossen ist. Das li-Element der aktiven Kategorie sollte mit der CSS-Klasse active und die li-Vorfahren der aktiven Kategorie mit activepath ausgezeichnet sein. (Diese Bezeichnungen können natürlich geändert werden. Das Script macht im übrigen keinen Unterschied zwischen active und activepath, allerdings dürfte es - aus Design/Usablility-Gründen - nahe liegen hier zu unterscheiden.)
Natürlich kann die HTML-Struktur erweitert werden. Z.B. unter Beachtung des Tutoriums für Barrierefreie Navigationsmenüs. Allerdings sollte das Trennzeichen (<span class="unsichtbar">. </span>) vor und nicht nach dem dem Link trennen, um den, im Stylesheet eingefügten, Folgeelement-Selektor nicht zu stören. (Dieser Selektor ist allerdings nicht zwingend notwendig und wird nur in richtigen Browsern (Firefox) bei ausgeschaltetem JavaScript und Tastaturnutzung genutzt.)
Das CSS ähnelt dem des Suckerfish Drop-Downs. Ihr seit bei der Designumsetzung extrem frei und könnt die Navigation nach Belieben gestalten. Nehmt euch ein Beispiel an den beiliegenden Stylesheets.
Das JS-Script muss grundsätzlich nicht verändert werden . Ihr könnt es folgendermaßen, nachdem DOM verfügbar ist, aufrufen.
var dhtmlmenu = new DickerFisch('Selektor der Navigation');
Das Script erwartet zwingend als ersten Parameter den CSS 1-3 Selektor des umschließenden Elements. (Ich empfehle aus Performance-Gründen diesem Element eine id zu geben und das Script mit dieser id aufzurufen). Daneben verfügt das Script über einen 2. optionalen Parameter, welcher verschiedenen Optionen bestückt werden kann.
var dhtmlmenu = new DickerFisch('#nav',{NavTimeout:300,ListType:'ol',Effect:2});
Live-Beispiel bei unserem Kunden der nachhaltigkeitsinitiative mit fluidem/liquidem und elastischem Design