onMouseover bei verschachtelten Elementen
Remo
- javascript
Hallo,
ich denke Probleme wie mein Folgendes sind hier schon hundertfach gestellt worden, allerdings habe ich trotz Suche hier im Forum nichts passendes gefunden.
Also, ich hab etwas in der Art:
<div class="outer" onMouseover="show('inner')" onMouseout="hide('inner')">
<a class="inner" href="">
<img alt="" border="0" src="images/an_image.gif">
</a>
</div>
D.h. bei onMouseover über das äussere div-Element wird der Link angezeigt. Sobald ich mit der Maus über das Bild im Link fahre, wird der onMouseout-Event getriggert und hide(inner) ausgeführt. Das wäre aber nicht das Ziel der Sache! Da 'outer' das 'inner'-Element umfasst, erwarte ich, dass selbst wenn ich mit der Maus auf einem inner-Objekt stehe, hide nicht ausgeführt wird, da ich noch innerhalb des 'outer'-Elements stehe. Klar was ich meine? ;)
Besten Dank für Hinweise,
Remo
Lieber Remo,
mit welchen Browsern hast Du Dein Script getestet? Verhalten sich alle getesteten Browser gleich?
Wo kann man sich das mal anschauen?
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Hallo,
D.h. bei onMouseover über das äussere div-Element wird der Link angezeigt. Sobald ich mit der Maus über das Bild im Link fahre, wird der onMouseout-Event getriggert und hide(inner) ausgeführt. Das wäre aber nicht das Ziel der Sache! Da 'outer' das 'inner'-Element umfasst, erwarte ich, dass selbst wenn ich mit der Maus auf einem inner-Objekt stehe, hide nicht ausgeführt wird, da ich noch innerhalb des 'outer'-Elements stehe. Klar was ich meine? ;)
Das Problem ist nicht trivial.
Zunächst einmal: hide() wird ausgeführt, weil mouseout-Events im Elementenbaum aufsteigen (bubbling). Das heißt konkret:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><title></title>
<script type="text/javascript">
[code lang=javascript]function log (message) {
if (typeof log.t == "undefined")
log.t = document.getElementById("t");
log.t.value = log.t.value + message + "\n";
}
function show (event) {
var target = event.target || event.srcElement;
log("over target: " + target.id + " > show");
document.getElementById("inner").style.visibility = "visible";
}
function hide (event) {
var target = event.target || event.srcElement;
log("out target: " + target.id + " > hide");
document.getElementById("inner").style.visibility = "hidden";
}
~~~</script>
<style type="text/css">
~~~css
#outer {width:200px; background-color:#fdd; padding:2em;}
#inner {background-color:#ddf; margin:auto; visibility:hidden;}
</style>
</head><body id="body">
<div id="outer" onmouseover="show(event)" onmouseout="hide(event)">
<div id="inner" href="">
<img id="bild" alt="" border="0" src="http://src.selfhtml.org/logo.gif">
</div>
</div>
<p><textarea id="t" cols="60" rows="25"></textarea></p>[/code]
Dieses Beispiel greift auf die Eigenschaft target bzw. srcElement des Event-Objektes zu, um in Erfahrung zu bringen, welcher Elementknoten der Ursprung des Events ist. Die Funktion log() verzeichnet alle mouseover- und mouseout-Mausereignisse in der Textarea.
Wenn man den Mauszeiger von außerhalb auf die Fläche des Elements #outer und von dort auf die Fläche des Elements #bild bewegt, passieren folgende Mausereignisse:
over target: outer > show
out target: outer > hide
over target: inner > show
out target: inner > hide
over target: bild > show
Wie man sieht, steigen die mouseover- und mouseout-Ereignisse bei Elementen innerhalb von #outer im Elementenbaum auf und lösen beim Element #outer die Handler show() und hide() aus.
Es entsteht dadurch ein überflüssiges Zeige- und Versteckspiel: Beim Übergang des Mauszeigers von der Fläche von #outer auf die Fläche von #inner wird zunächst onmouseout bei #outer gefeuert. Dadurch wird #inner versteckt. Im gleichen Moment wird aber mouseover bei #inner ausgelöst. Dieser Event steigt auf und löst den mouseover-Handler von #outer aus. Es wird also show() aufgerufen und #inner bleibt letztlich sichtbar. Dasselbe Spiel beim Übergang von #inner auf #bild.
Nun, dieses Spiel ist zwar überflüssig, es führt aber dazu, dass das Einblenden und Ausblenden von #inner letztlich durchaus funktioniert. Was ist also das Problem?
Dieses überflüssige Spiel kann man natürlich verhindern. Zuerst einmal sollten die mouseover- und mouseout-Handler von #outer nur Events verarbeiten, deren Ursprung #outer selbst ist. Damit kann man die meisten Ereignisse in obiger Liste ignorieren.
Es bleibt aber »out target: outer > hide« beim Übergang des Mauszeigers von #outer nach #inner. Woher weiß man, dass sich der Mauszeiger im Grunde gar nicht aus der #outer-Box herausbewegt? Der Lösungsansatz ist: Man fragt in der hide()-Funktion ab, welchem Element die Fläche gehört, auf das sich der Mauszeiger mit dem Verlassen der Elementfläche bewegt. Dabei helfen die Eigenschaften relatedTarget bzw. toElement des Event-Objekts weiter. Ob das Ursprungselement in #outer drinsteckt oder nicht, lässt sich mit contains() in Erfahrung bringen. Dies ist aber eine Microsoft-Erfindung ist, die zumindest Gecko nicht kennt. Man kann aber auch einfach die ID, Klasse oder den parentNode abfragen.
Beim Übergang des Mauszeigers von #bild auf die fläche außerhalb von #outer passieren folgende Ereignisse:
out target: bild > hide
over target: inner > show
out target: inner > hide
over target: outer > show
out target: outer > hide
Hierbei filtert die Begrenzung auf #outer als Ursprungselement alle Ereignisse bis auf »over target: outer > show«. Dieses kann man analog mit relatedTarget bzw. fromElement filtern. Darin wird bei einem mouseover-Ereignis gespeichert, welchem Element die Fläche gehört, von der der Mauszeiger kommt. Wenn dieses Element #inner ist, muss show() das Ereignis ignorieren.
Alles zusammengewürfelt sieht führt uns zu folgenden Funktionen aus:
function show (event) {
var target = event.target || event.srcElement || false;
var fromElement = event.relatedTarget || event.fromElement || false;
if (target.id == "outer" && fromElement.id != "inner") {
log("over target: " + target.id + " from: " + fromElement.id + " > show");
document.getElementById("inner").style.visibility = "visible";
} else {
log("over target: " + target.id + " from: " + fromElement.id + " > ignore");
}
}
function hide (event) {
var target = event.target || event.srcElement || false;
var toElement = event.relatedTarget || event.toElement || false;
if (target.id == "outer" && toElement.id != "inner") {
log("out target: " + target.id + " to: " + toElement.id + " > hide");
document.getElementById("inner").style.visibility = "hidden";
} else {
log("out target: " + target.id + " to: " + toElement.id + " > ignore");
}
}
Damit müssen alle unwichtigen Ereignisse ignoriert werden. Beispiel des Bewegens von Außen zum Bild und wieder zurück:
over target: outer from: body > show
out target: outer to: inner > ignore
over target: inner from: outer > ignore
out target: inner to: bild > ignore
over target: bild from: inner > ignore
out target: bild to: inner > ignore
over target: inner from: bild > ignore
out target: inner to: outer > ignore
over target: outer from: inner > ignore
out target: outer to: body > hide
Grüße,
Mathias
Mathias,
super Antwort, danke dass Du Dir Zeit dafür genommen hast!
Remo
Hi Remo,
ich denke Probleme wie mein Folgendes sind hier schon hundertfach gestellt worden, allerdings habe ich trotz Suche hier im Forum nichts passendes gefunden.
Weil du nach JavaScript gesucht hast, und das ein CSS-Problem ist.
D.h. bei onMouseover über das äussere div-Element wird der Link angezeigt. Sobald ich mit der Maus über das Bild im Link fahre, wird der onMouseout-Event getriggert und hide(inner) ausgeführt. Das wäre aber nicht das Ziel der Sache! Da 'outer' das 'inner'-Element umfasst, erwarte ich, dass selbst wenn ich mit der Maus auf einem inner-Objekt stehe, hide nicht ausgeführt wird, da ich noch innerhalb des 'outer'-Elements stehe. Klar was ich meine? ;)
Ja. Das kann man wunderbar mit CSS lösen:
inner in outer: nicht anzeigen
inner in mausdrauf-outer: anzeigen
http://de.selfhtml.org/css/formate/zentrale.htm#verschachtelte_elemente
http://de.selfhtml.org/css/formate/zentrale.htm#pseudoformate (:hover)
Gruß, Marian