tomtaylor: jQuery Hovermenü - Delay einbauen?

Hallo,

ich habe folgendes Problem: Ich will ein Menü öffnen sobald man mit der Maus über einen Link hovert. Solange sich die Maus dann auf diesem Menü befindet, soll es natürlich auch noch geöffnet bleiben. Das ganze habe ich so realisiert, dass bei einem Hover über den Link und über das Menü, dieses eingeblendet wird, in der Callbackmethode wird es wieder ausgeblendet.

Das funktioniert soweit auch gut. Das Problem ist jetzt nur: Wenn man über den Link hovert, sich das Menu öffnet und man die Maus auf dem direkten Weg nach unten in den unteren Bereich des Menüs bewegt, befindet sich der Mauszeiger für kurze Zeit nichtmehr über Link und nichtmehr über dem Menü -> Menü wird ausgeblendet.

Jemand ne Idee, wie ich das Problem lösen kann? Mit einem Delay/Timeout beim hide?

Hier mal noch mein Code:

<!DOCTYPE html>  
  
<html>  
  
	<head>  
	  
		<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>  
		<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script>  
		  
		<style>  
			a{  
				float:left;  
			}  
			.menu{  
				background:grey;  
				width:100px;  
				height:400px;  
				float:left;  
				display:none;  
			}  
		</style>  
	</head>  
	  
	<body>  
		<a href="#">link</a>  
		  
		<div class="menu"></div>  
	  
	  
		<script type="text/javascript">  
  
			$("a").hover(function(){  
				showMenu();  
			}, function(){  
				hideMenu();  
			});  
			  
			$(".menu").hover(function(){  
				showMenu();  
			}, function(){  
				hideMenu();  
			});  
			  
			function showMenu(){  
				$(".menu").show();  
			}  
			  
			function hideMenu(){  
				$(".menu").hide();  
			}  
  
		</script>  
	</body>  
	  
</html>
  1. Hi,

    Das Problem ist jetzt nur: Wenn man über den Link hovert, sich das Menu öffnet und man die Maus auf dem direkten Weg nach unten in den unteren Bereich des Menüs bewegt, befindet sich der Mauszeiger für kurze Zeit nichtmehr über Link und nichtmehr über dem Menü

    Wieso nicht - gibt's es da „Lücken“?

    Jemand ne Idee, wie ich das Problem lösen kann? Mit einem Delay/Timeout beim hide?

    Wenn's an Lücken liegen sollte - mit einem vernünftigeren Menü(-aufbau).

    Andernfalls, wenn lediglich das Feuern von mouseout-Events beim Überfahren eines anderen Elements innerhalb des Menüs das Problem auslöst - auf mouseenter/mouseleave umsteigen.

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. Hi,

      Das Problem ist jetzt nur: Wenn man über den Link hovert, sich das Menu öffnet und man die Maus auf dem direkten Weg nach unten in den unteren Bereich des Menüs bewegt, befindet sich der Mauszeiger für kurze Zeit nichtmehr über Link und nichtmehr über dem Menü

      Wieso nicht - gibt's es da „Lücken“?

      Jemand ne Idee, wie ich das Problem lösen kann? Mit einem Delay/Timeout beim hide?

      Wenn's an Lücken liegen sollte - mit einem vernünftigeren Menü(-aufbau).

      Andernfalls, wenn lediglich das Feuern von mouseout-Events beim Überfahren eines anderen Elements innerhalb des Menüs das Problem auslöst - auf mouseenter/mouseleave umsteigen.

      MfG ChrisB

      Ich habe das Problem mal versucht mit einem Bild zu verdeutlichen: Ich hover über den Link und will ganz unten im Menu etwas anklicken. Die Rote Linie stellt dabei die Mausbewegung da... und befindet sich teilweise nichtmehr über dem Link und nicht über dem Menü

      menu

      1. Hi,

        Ich habe das Problem mal versucht mit einem Bild zu verdeutlichen: Ich hover über den Link und will ganz unten im Menu etwas anklicken. Die Rote Linie stellt dabei die Mausbewegung da... und befindet sich teilweise nichtmehr über dem Link und nicht über dem Menü

        menu

        Also ein aus Usability-Sicht fragwürdiger Aufbau des ganzen, wie ich schon vermutet hatte.

        Entweder suchst du dir eine andere Möglichkeit, das umzusetzen, die weniger ein Geschicklichtkeitsspiel darstellt - oder du wirst tatsächlich mit einem Delay arbeiten müssen.

        MfG ChrisB

        --
        RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
        1. An diesem Konzept lässt sich wahrscheinlich nichtmehr ändern. Daher müsste ich das mit dem Delay irgendwie hinbekommen.

          Wenn ich ein $(".menu").delay(4000).hide(); verwende wird dieses Delay aber scheinbar einfach ignoriert. Kann mir jemand sagen warum das geht nicht, und wie ich es richtig mache?

          Hi,

          Ich habe das Problem mal versucht mit einem Bild zu verdeutlichen: Ich hover über den Link und will ganz unten im Menu etwas anklicken. Die Rote Linie stellt dabei die Mausbewegung da... und befindet sich teilweise nichtmehr über dem Link und nicht über dem Menü

          menu

          Also ein aus Usability-Sicht fragwürdiger Aufbau des ganzen, wie ich schon vermutet hatte.

          Entweder suchst du dir eine andere Möglichkeit, das umzusetzen, die weniger ein Geschicklichtkeitsspiel darstellt - oder du wirst tatsächlich mit einem Delay arbeiten müssen.

          MfG ChrisB

          1. Hi,

            Wenn ich ein $(".menu").delay(4000).hide(); verwende wird dieses Delay aber scheinbar einfach ignoriert.

            Ich bin mir nicht sicher, ob delay hier geeignet ist:
            "The .delay() method is best for delaying between queued jQuery effects. Because it is limited — it doesn' offer a way to cancel the delay"
            Schnelle Mousbewegungen würden damit zu einer Überlagerung führen.

            Vorgehensweise:
            Auf entsprechenden Event (mouseout/mouseleave) startest Du einen "echten" Timeout. Jeder neue Event muss diesen Timeout zunächst stoppen, dann erneut starten. Wenn der Timeout abgelaufen ist führst Du den eigentlich gewünschten Prozess zum Verstecken durch.

            Gruesse, Joachim

            --
            Am Ende wird alles gut.
            1. "The .delay() method is best for delaying between queued jQuery effects. Because it is limited — it doesn' offer a way to cancel the delay"

              Sicher - delay() wird in die Effekt-Queue eingefügt und diese lässt sich problemlos überspringen - z.B. mit einem .stop(true, true) wenn man über einen anderen Menüpunkt "überfährt". Die Doku ist hier etwas unglücklich geschrieben.

              Schnelle Mousbewegungen würden damit zu einer Überlagerung führen.

              Siehe oben.

              Auf entsprechenden Event (mouseout/mouseleave) startest Du einen "echten" Timeout. Jeder neue Event muss diesen Timeout zunächst stoppen, dann erneut starten. Wenn der Timeout abgelaufen ist führst Du den eigentlich gewünschten Prozess zum Verstecken durch.

              genau das macht delay() und stop(), nur dass dann alles sauber ein der Queue hängt.

              1. Hi Suit,

                Die Doku ist hier etwas unglücklich geschrieben.

                Das ist etwas unglücklich ausgedrückt ;-)

                Denn der komplette Warnhinweis sagt sogar:
                ".delay() is not a replacement for JavaScript's native setTimeout function, which may be more appropriate for certain use cases."

                Insofern ist die Doku da schlicht ..falsch? ..überholt?

                Gruesse, Joachim

                --
                Am Ende wird alles gut.
                1. Die Doku ist hier etwas unglücklich geschrieben.
                  Das ist etwas unglücklich ausgedrückt ;-)

                  :)

                  Denn der komplette Warnhinweis sagt sogar:
                  ".delay() is not a replacement for JavaScript's native setTimeout function, which may be more appropriate for certain use cases."

                  Welche Fälle das sind, ist aber nicht beschrieben :)

                  Insofern ist die Doku da schlicht ..falsch? ..überholt?

                  Jein - delay() wird an die Standard-Effekt-Warteschleife angehängt (wenn man das 2. Argument nicht angibt) - setTimeout läuft parallel unabhängig von dieser Schleife.

                  Im beschriebenen Fall des OP will er aber genau vor der hide()-Methode in die Warteschleife - also kein Grund, parallel dazu ein setTimeout mitlaufen zu lassen.

                  Was mit dem Satz in der Doku wahrscheinlich gemeint ist: innerhalb einer bestehenden Queue kann man ein einzelnes Delay nicht entfernen.

                  Die Doku ist ein einigen Punkten ein bisschen widersprüchlich - sie sagt z.B. auch, dass man mit stop() nur bei Animationen zum Ende springen kann und .clearQueue() zum Entfernen gedacht ist. delay() ist aber offensichtlich keine Animation und lässt sich mit stop() wunderbar überspringen.

                  Bug, Feature?

    2. Andernfalls, wenn lediglich das Feuern von mouseout-Events beim Überfahren eines anderen Elements innerhalb des Menüs das Problem auslöst - auf mouseenter/mouseleave umsteigen.

      Wozu? Das macht hover() doch schon alles zuverlässig - daran liegt es sicher nicht :)

  2. Kannst ja mal folgenden Code verwenden.
    Habe es gerade so auf die schnelle runtergerotzt.
    Ist sicherlich auch noch ausbaufähig, ausserdem verwende ich hierbei globale Varibalen, was sicherlich auch nicht ganz sauber ist.

      
    <script type="text/javascript">  
    	  
     var do_hide = true; //Zustandsvariable  
     var time = 2000; //x Millisekunden bis das Menu verschwindet  
    	  
     $("a").hover(function(){  
       do_hide = false;  
       showMenu();  
      }, function(){  
       do_hide = true;  
       window.setTimeout("hideMenu()", time);  
     });  
    	  
     $(".menu").hover(function(){  
       do_hide = false;  
       showMenu();  
     }, function(){  
       do_hide = true;  
       window.setTimeout("hideMenu()", time);  
     });  
    	  
     function showMenu(){  
       $(".menu").show();  
     }  
    	  
     function hideMenu(){  
       if(do_hide)  
       {  
         $(".menu").hide();  
       }  
     }  
      
    </script>  
      
    
    
    1. Hi,

      Habe es gerade so auf die schnelle runtergerotzt.

      Merkt man: Timeouts sollte man per Handler immer stoppen, bevor man sie startet. Sonst laufen nachher ein paar nebeneinander her...

      Gruesse, Joachim

      --
      Am Ende wird alles gut.
      1. Hi,

        Habe es gerade so auf die schnelle runtergerotzt.
        Merkt man: Timeouts sollte man per Handler immer stoppen, bevor man sie startet. Sonst laufen nachher ein paar nebeneinander her...

        Gruesse, Joachim

        Ohh, da hast du vollkommen recht. Und wieder was dazu gelernt. Danke dir :)

    2. Kannst ja mal folgenden Code verwenden.

      Immer gut, wenn man ein Framework verwendet und dann schön dran vorbeiprogrammiert - so gehört sich das.

      Habe es gerade so auf die schnelle runtergerotzt.

      Ja, sieht man.

      Ist sicherlich auch noch ausbaufähig,

      Ich würde sagen, dass man das Ding eher in die Tonne treten sollte - wenn du eine Lösung ohne jQuery machen willst, ist das vertretbar - aber wie schon gesagt: Framework verwenden oder nicht.

      			$(document).ready(function() {  
      				$('#menu>ul>li').hover(  
      					function() {  
      						$('#menu>ul>li>ul').stop(true, true).hide();  
      						$('ul', this).show();  
      					},  
      					function() {  
      						$('ul', this).delay(4000).hide();  
      					}  
      				);  
      			});
      

      Wie ich schon sagte - die jQuery Doku ist etwas unglücklich geschrieben, wenn es um delay() geht.

      Im ersten Argument von hover() werden alle Menüpunkte versteckt (und zum Ende der Queue gesprungen - das beendet auch das delay()).

      Danach wird der derzeitige geöffnet.

      Beim Verlassen (2. Argument) wird ein delay() gesetzt und dann erst geschlossen.

      Aber auch das funktioniert noch nicht einwandfrei - es dürfen natürlich im ersten Argument nicht alle geschlossen werden, sondern nur diejenigen, die nicht gewählt wurden. Das delay() muss hier natürlich trotzdem beendet werden, aber der Punkt muss geöffent bleiben, wenn es der zur Zeit gewählte ist - sonst klappt der zu und sofort wieder auf.

      Aber das kriegt der OP sicher alleine hin. Man darf ja nicht alles vorbeissen hier - aber zumindest sollte der OP mit der richtigen Ausgangsbasis weitermachen.

      Die showMenu- und hideMenu-Funktionen sind übrigens unfug, die hab' ich auch gleich entsorgt.

      1. Hi Suit,

          			function() {  
          				$('ul', this).delay(4000).hide();  
          			}  
        

        hm, das Beispiel funktioniert für mich nicht, Erklärung glaube ich hier gefunden zu haben:
        "Only subsequent events in a queue are delayed; for example this will not delay the no-arguments forms of .show() or .hide() which do not use the effects queue."

        mit fadeOut würde es auch funktionieren, verhindert aber nur vorzeitiges Ausblenden bei Bewegung über "Leerraum", streife ich versehentlich einen anderen Menüpunkt, wird dieser natürlich sofort eingeblendet. Insofern entsteht imho nicht ganz der gewünschte Effekt einer generellen Verzögerung.

        Gruesse, Joachim

        --
        Am Ende wird alles gut.
        1. und nochmal hi,

          Insofern entsteht imho nicht ganz der gewünschte Effekt einer generellen Verzögerung.

          Und weil mir "delay" hier etwas spröde erscheint, stelle ich eine Version mit setTimout zu Diskussion, auch wenn Du mich ob der Vermengung von Framework und Classic-Js schlägst. Ich gestehe, dass ich zu ungeduldig war, um das jetzt mit jquery-Bordmitteln hinzupopeln...

          Gruesse, Joachim

          --
          Am Ende wird alles gut.
        2. "Only subsequent events in a queue are delayed; for example this will not delay the no-arguments forms of .show() or .hide() which do not use the effects queue."

          Ja, da hast du recht - das kommt davon, wenn man nicht vorher testet :)

          Dann schreibst du halt .show(10) und .hide(10) und die sache hat sich :p

    3. Hallo,
      muss das Menü eigentlich mit jQuery gebaut werden? Bei Menüs wäre ich da sehr vorsichtig. Wenn jemand JavaScript nicht aktiviert hat, sieht er gar kein Menü....!
      Das geht mit CSS wesentlich einfacher und ist damit auch ohne JavaScript
      nutzbar.

      LG
      toni

      1. Hallo,
        muss das Menü eigentlich mit jQuery gebaut werden? Bei Menüs wäre ich da sehr vorsichtig. Wenn jemand JavaScript nicht aktiviert hat, sieht er gar kein Menü....!
        Das geht mit CSS wesentlich einfacher und ist damit auch ohne JavaScript
        nutzbar.

        LG
        toni

        Hier habe ich mal ein Beispiel mit CSS:
        Zuerst mal html-Code. Es handelt sich hier um eine Liste mit nur einem Element. Innerhalb des li-Elements befindet sich ein Container mit dem Namen sub der eine bestimmte Formatierung hat. In diesem sind dann weitere Links mit Formatierungen enthalten. Geht der User jetzt mit der Maus über "Menü" öffnet sich per css das Unternmenü und er kann mit der Maus nach unten gehen und Link oder Link2 anklicken.

        <ul id="navigation">  
                        <li><a href="">Menü</a>  
                            <div class="sub">  
          
                                <div class="cont">  
                                    <p>Text der den Link erläutert</p>  
                                    <a href="xy.html">&raquo; Link</a>  
                                </div>  
          
                                 <div class="cont">  
                                   <p>Text der den Link erläutert</p>  
                                    <a href="xcy.html">&raquo; Link2</a>  
                                </div>  
          
                                <br class="stopper" />  
                            </div>  
                        </li>  
          
                    </ul>
        

        Hier das CSS (das kann man sicher schöner machen und anders lösen - aber es funktioniert). Da sind jetzt natürlich noch Formatierungen drin, die ich aus einem Projekt noch drin habe - aber das sollte nich stören.
        Im Grunde geht es so, dass der ganze Container unterhalb des li erstmal nicht sichtbar ist. Wenn man jetzt mit der Maus über das Menü geht, wird der Container auf display:block geschaltet und man sieht alles. Da der Container innerhalb des li liegt, ist die ganze Fläche auch aktiv und verschwindet nicht wenn man die Maus vom Menü nach unten bewegt.
        Wie gesagt - klappt auch ohne JS.

          
        ul#navigation {  
            position:absolute;  
            width:239px;  
            height:auto;  
            left:0;  
            top:0;  
            z-index:2000;  
        }  
          
        ul#navigation li {  
            float:left;  
            position:relative;  
            background:#e50050;  
        }  
          
        ul#navigation li a {  
            display:block;  
            width:239px;  
            height:23px;  
            padding:7px 15px 0 10px;  
            color:#fff;  
            font-size:12px;  
            font-weight:bold;  
            text-decoration:none;  
        }  
          
        ul#navigation .sub {  
            position:absolute;  
            display:none;  
            left:0;  
            top:30px;  
            background:#e1e1e1;  
            width:960px;  
            height:400px;  
            border-top:1px solid #383733;  
            z-index:70;  
        }  
          
        ul#navigation .sub .cont {  
            float:left;  
            margin:25px;  
            padding:10px;  
            width:170px;  
            height:125px;  
            background:#fff;  
        }  
          
        ul#navigation .sub .cont a {  
            margin:30px 0 0 0;  
            color: #000;  
            font-size: 16px;  
            font-weight: bold;  
            padding:15px 0 0 0;  
        }  
          
          
        ul#navigation .sub .cont p {  
            font-size:12px;  
            color:#000;  
            font-weight:normal;  
            height:40px;  
        }  
          
          
        ul#navigation .sub .cont a:hover {  
            color:#e50050;  
        }  
          
          
        ul#navigation li:hover .sub {  
           display:block;  
        }  
          
          
        
        

        LG
        Toni

        1. Hier habe ich mal ein Beispiel mit CSS:

          Du meinst ein Beispiel das man möglichst nicht nachmachen sollte - es ist schlichtweg grauenhaft. Was soll diese ungustiöse div-Suppe darstellen?