Nette Erweiterung für jedes Menü mit jQuery
Es gibt kaum noch ein Menü, welches den Nutzer nicht mit einem Mouseover-Status über seine aktuelle Position informiert. Soweit so gut. Meistens sieht es so aus: Der Nutzer klickt auf den Menüpunkt, der Mouseover-Status verschwindet, die Seite verschwindet, die Seite baut sich wieder auf und der eben geklickte Menüpunkt ist dauerhaft hervorgehoben.
Ganz normal. Aber: Mit ein bisschen jQuery lässt sich der Mouseover-Status schon bei einem Klick festsetzen. Das sieht gut aus und wirkt fast wie ein dynamisches nachladen der Seite.
Ansehen kann man sich diesen Effekt zum Beispiel hier im Hauptmenü oder auf der Seite von Isabel von Roon. Wie der Effekt eingebaut wird, was find(), filter() und siblings() bedeutet und wie man den aktiven Menüpunkt hervorhebt wird in diesem Artikel besprochen.
Die HTML Grundage
Das oben genannte Beispiel ist zu simpel, deshalb machen wir es etwas komplizierter undzwar mit einem verschachtelten Menü.
<div id="menu">
<ul>
<li><a href="#">Punkt 1</a></li>
<li>
<a href="#">Punkt 2</a>
<ul>
<li><a href="#">Punkt 2.1</a></li>
<li><a href="#">Punkt 2.2</a></li>
<li><a href="#">Punkt 2.3</a></li>
</ul>
</li>
<li>
<a href="#">Punkt 3</a>
<ul>
<li><a href="#">Punkt 3.1</a></li>
<li><a href="#">Punkt 3.2</a></li>
<li><a href="#">Punkt 3.3</a></li>
<li><a href="#">Punkt 3.4</a></li>
</ul>
</li>
<li><a href="#">Punkt 4</a></li>
</ul>
</div>
Die Funktionsweise ist klar: Bei einem Klick ins Menü darf sich nur das jeweilige Menü verändern (die Nachbarn). Einfach die Aktiv-Klasse generell zu entfernen und dem angeklickten Menüpunkt zuweisen hätte zur Folge, dass beim Klick ins Submenü der darüber liegende Punkt seine Aktiv-Klasse verlieren würde.
Demo: Hier ansehen was ich meine.
Lovley jQuery
Wer jetzt an ein kompliziertes und umfangreiches Script denkt, der hat noch nicht genug mit jQuery gearbeitet. Denn die Anforderungen benötigen nur eine einzige Zeile:
jQuery(function($) {
$('#menu a').click(function() {
$(this).blur().parents('li').siblings('li:has(.active)').find('a').removeClass('active').end().end().end().addClass('active');
return false;
});
});
Was hier passiert ist wird jetzt Stück für Stück durchleuchtet:
jQuery(function($) {})
Diese Funktion wird aufgerufen, sobald das DOM fertig geladen wurde. Als Parameter wird das jQuery-Framework übergeben und in der Variable $ gespeichert. Mit dieser Schreibweise hat man weniger Stress, wenn ein Kollege auf die Idee kommt ein zweites Javascript Framework einzubinden (Grrr...)
$('#menu a').click(function() {})
Mit dieser Anweisung werden alle Links im Element mit der ID Menü selektiert und jeder einzelne bekommt einen Eventhandler zugewiesen: click.
In der Funktion ist der angeklickte Link in der Variable this gespeichert.
$(this)
Mit diesem Konstrukt wird der angeklickte Menüpunkt in ein jQuery-Objekt verwandelt, und liegt als einziges auf dem Elementstack.
.blur()
entfernt den gepunkteten Rahmen um den Link. Eine alternative Möglichkeit wäre outline:none im CSS anzugeben. Je nach belieben.
.parents('li')
Jetzt wird es spannend! Diese Funktion wählt alle Elternelemente aus. Mit der Einschränkung, dass sie vom Typ li sind.
.siblings('li:has(.active)')
Siblings steht für Nachbarn. Diese Funktion findet nur die benachbarten Elemente. Klickt man also auf ein Untermenü, befinden sich nach siblings() alle LI Elemente dieser Liste im Stack.
Mit der Übergabe eines Selectors kann man die Nachbarn auch gleich einschränken. li:has('.active') steht für alle LI Elemente welche ein Objekt der Klasse .active beinhalten.
Alternativ könnte man auch die Funktion filter() benutzen. Mit siblings('li') werden alle benachbarten LI Elemente ausgewählt. Anschließend mit filter(':has('.active')') auf das enthalten sein eines aktiven Links geprüft. Und genau hier liegt der Unterschied zwischen filter() und find():
Filter() filtert die Elemente im Stack. Find() sucht innerhalb der Elemente im Stack. Logisch, oder?
Mit siblings('li:has(.active)') sparen wir uns eine Anweisung und im Stack befinden sich alle benachbarten Elemente, die einen aktiven Link beinhalten. Weiter gehts.
.find('a')
Innerhalb des LI Elements wird jetzt nach dem aktiven Link gesucht und dieser ausgewählt.
.removeClass('active')
Entfernt die Klasse von dem Link. (Bitte daran denken keinen Punkt vor der Klasse anzugeben!)
.end()
Mit der Funktion end() wird eine Veränderung im Elementstack rückgängig gemacht. Dabei bezieht sich das end() immer auf die als letztes getätigten Veränderungen.
Das erste end() macht also das find('a') rückgängig, im Stack befinden sich jetzt wieder die LI Elemente mit einem (ehemals) aktiven Link.
.end()
Als nächstes werden die Nachbarn aus dem Stack geworfen. (siblings)
.end()
Und jetzt noch die Elternelemente raus und wieder zurück zum Kind: $(this). Wir befinden uns jetzt wieder ganz am Anfang der Auswahl und es bleibt nur noch eine Funktion:
.addClass('active')
Diese Funktion weißt dem angeklickten Link die passende Klasse zu, damit beim nächsten Klick das ganze Spiel wieder von vorne beginnen kann.
Das wars!
Im Beispiel habe ich noch ein return false; hinzugefügt. Diese Anweisung verhindert, dass der Browser sein Standardverhalten abspielt (bei Links die angeklickte Seite aufrufen). In einer wirklichen Umgebung darf das natürlich nicht vorkommen.
Ich hoffe ihr konntet etwas aus diesem Artikel mitnehmen. Weitere jQuery Fragen und Beispiellösungen stelle ich gerne auf Anfrage vor. Nur rein in die Kommentare.
Frohes verketten!
Danke für den Artikel. Endlich mal eine anschauliche Darstellung was man mit jQuery in einer Zeile honbekommen kann.
Grüße Daniel
Danke für dieses nette Beispiel! Mich – als jQuery beginner – hätte nun interessiert, wie das mit den Aktivstati ist, wenn die Seite nun doch reloaded wird. Gibt es eine Möglichkeit, die aktuelle Konfiguration des Menüs über einen Seitenreload “mitzunehmen” und dann erneut herzustellen? Arbeite mit Typo3, aber derzeit noch ohne irgendwelche JS oder AJAX, möchte aber gerne…
Danke!
Vielen Dank für das Lob!
@Robert: Du kannst in TYPO3 sehr genau bestimmen, wie das Menü gerendert wird. Über die Eigenschaft ACT kannst du den aktuellen Menüpunkt beschreiben. Hier gibste einfach die Klasse mit.
Zum Beispiel mit MENU.1.ACT.ATagParams = class=”active”
Das hat dann gar nichts mehr mit jQuery zu tun.
Danke für deine Antwort! Ich hab mich natürlich unklar ausgedrpückt – darf ich nochmals probieren?
Mit “meinen” Activ-Stati meine ich 3 Container, wovon der angeklickte sich vergrößern und dann das Menü anzeigt. Klicke ich nun auf einen Menüpunkt, gibt es einen Seitenreload und alle COntainer sind wieder so wie anfangs (außer ich lese die aktive PDinRootline ein und verknüofe damit ein spezifische CSS). Ich hätte aber natürlich gerne, dass der aktive Container die größe seines Zustands behält.
Ich “richte” irgendwie, dass ich mit jQuery alle a aus den jeweiligen Containern holen und das defaultBehaviour dieser a verhinden muss – dann wird alles AJAX oder so? Hmm… mal sehen.
Jeder Tipp ist natürlich sehr willkommen!
LG, R
Hi Robert, so ganz genau verstanden habe ich es ehrlich gesagt nicht, aber Tipps gebe ich immer gerne:
1. Du kannst das Standardverhalten von Links sehr einfach verhindern in dem du “false” zurück gibst: $(”.menu a”).click(function() {
….
return false;
});
2. Wenn der angeklickte Link nach dem Reload die Klasse “active” erhalten hat, kannst du mit jQuery darauf reagieren und zum Beispiel weitere Klicks auslösen:
$(document).ready(function() {
$(”.menu div”).click(function() …. //das ist die normale funktion
//das löst einen klick darauf aus
$(”.menu div>a.active”).parent().click();
});
Dieses Script “klickt” also das umfassende DIV eines aktiven Menüpunkts an.
Hilft dir das weiter?
Danke! Ich glaube, da hilft sicher was! Vielen Dank für deine Tipps!!! Ich werde das heute noch ausprobieren und halte dich am Laufenden! Schönen Sonntag noch!
alert(’Cross Side Scripting’)
Hallo,
nette Angelegenheit … habe ich direkt in eine Seite verbaut und passe es noch etwas an für ein Suckerfish-Menü über 3 Ebenen.!
Statt “siblings(’li:has(.active)’)” sollte siblings(’li.active’) reichen … entweder befindet sich auf der jeweiligen Ebene ein li/a-tag mit der Klasse ‘active’, die angesprochen werden kann oder halt nicht.
Gruß Matu
@matu ja, du hast recht sofern das li die Klasse “active” mitbekommt. In meinem Beispiel befindet sich im Listenelement ein Link mit der entsprechenden Klasse. Deshalb das :has(.active).
@test ein bisschen ZU einfach. Aber Danke für das Interesse.
Hallo,
ich habe mal versucht, Dein Beispiel auf meine Seite anzupassen, leider ohne Erfolg
(bin allerdings auch noch ein JQuery-Newbie…
Meine Seite verlinkt sämtliche Inhalte auch per jOuery (in verschiedene DIVs). Ich nehme
mal an, dass aus diesem Grund der css-Selektor a:active bei mir nicht funktioniert (die
aktiven Links werden als nicht-aktiv angezeigt).
Wie kann ich Dein Beispiel evtl. umbauen, damit dieses für alle a-Tags gilt und damit
die Links auch als active dargestellt werden?
Hier mal eine Code-Zeile aus der Navi:
LEISTUNGEN
Und hier die JavaScript-Funktion “internal_link()”:
function internal_link(locA,locB) {
jQuery(document).ready(function($){
if (locA == ”) {
$(”#unternavi”).load(’leer.html’);
$(”#inhalt”).load(locB);
} else {
$(”#unternavi”).load(locA);
$(”#inhalt”).load(locB);
}
});
}
Vielen Dank schon mal für die Hilfe !!!
Gruß, Michael