読者です 読者をやめる 読者になる 読者になる

jQuery の hover() について調べたことのまとめ

hover() は mouseenter と mouseleave を同時にセットする

http://docs.jquery.com/Events/hover の引数の名前付けを見て、hover は mouseover と mouseout を同時に指定するものだと思っていたが違うらしい。

jquery-1.2.6.js の 2278 行付近
	hover: function(fnOver, fnOut) {
		return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
	},

とあるように mouseenter と mouseleave に対して指定するものである。
over/outと enter/leave の違いは、http://docs.jquery.com/Events/mouseover の Demo みるとよくわかる。
ある領域 A にカーソルが載ったときに、領域 A 内に動的にブロック B を表示するという処理を次のようなに作っていた。

http://tilfin.googlepages.com/overout.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="content-script-type" content="text/javascript">
<script src="jquery-1.2.6.js"></script>
<title>jquery mouseover / mouseout</title>
<style type="text/css"><!--
#A {
 color:#fff;
 background-color:blue;
 border:1px solid #000;
 width:300px;
 height:200px;
 position:relative;
}

#B {
 color:#fff;
 background-color:red;
 border:1px solid #000;
 display:none;
 width:200px;
 height:100px;
 position:absolute;
 bottom:0;
 right:0;
}

#debug p {
 margin:0;
 line-height:110%;
 font-size:9pt;
}
//--></style>
<script><!--
$(document).ready(function(){
  $("#A")
    .mouseover(function(){
       $("#debug").append("<p>A: mouseover</p>");
       $("#B").appendTo(this).show();
    })
    .mouseout(function(){
       $("#debug").append("<p>A: mouseout</p>");
       $("#B").hide().appendTo(document.body);
    });
});
//--></script>
</head>
<body>
<div id="A">A</div>
<div id="B">B</div>
<div id="debug"></div>
</body>
</html>

しかし、これだと表示されたブロック B にカーソルが載ったとき、A の mouseout が発生するためちらつきが起こってしまう(これは Firefox のときで IE ではちらつきではなく消えたままになった)。

http://tilfin.googlepages.com/enterleave.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="content-script-type" content="text/javascript">
<script src="jquery-1.2.6.js"></script>
<title>jquery mouseenter / mouseleave</title>
<style type="text/css"><!--
#A {
 color:#fff;
 background-color:blue;
 border:1px solid #000;
 width:300px;
 height:200px;
 position:relative;
}

#B {
 color:#fff;
 background-color:red;
 border:1px solid #000;
 display:none;
 width:200px;
 height:100px;
 position:absolute;
 bottom:0;
 right:0;
}

#debug p {
 margin:0;
 line-height:110%;
 font-size:9pt;
}
//--></style>
<script><!--
$(document).ready(function(){
  $("#A").hover(function(){
       $("#debug").append("<p>A: mouseenter</p>");
       $("#B").appendTo(this).show();
    }, function(){
       $("#debug").append("<p>A: mouseleave</p>");
       $("#B").hide().appendTo(document.body);
    });
  /*
  $("#A")
    .bind("mouseenter", function(){
       $("#debug").append("<p>A: mouseenter</p>");
       $("#B").appendTo(this).show();
    })
    .bind("mouseleave", function(){
       $("#debug").append("<p>A: mouseleave</p>");
       $("#B").hide().appendTo(document.body);
    });
  */
});
//--></script>
</head>
<body>
<div id="A">A</div>
<div id="B">B</div>
<div id="debug"></div>
</body>
</html>

として hover (mouseenter/mouseleave) を使うとうまくいった。
これは jquery のソースをみると下記のようにサブ要素上にまだあるかイベント内でフックして判定しているからである。

jQuery.event.special.mouseleave.handler
	handler: function(event) {
		// If we actually just moused on to a sub-element, ignore it
		if ( withinElement(event, this) ) return true;
		// Execute the right handlers by setting the event type to mouseenter
		event.type = "mouseenter";
		return jQuery.event.handle.apply(this, arguments);
	}

ちなみにこのような動的な方法ではなく、元々

と書いてある場合は、上記の例(over/out も enter/leave)どちらでも動作する。

hover を unbind する方法

hover はイベントタイプとして存在するわけではないので、$("#A").unbind("hover") とは書けない。

$("#A").unbind("mouseover").unbind("mouseout");

とするのも駄目である。これだと前述のように、mouseenter と mouseleave からイベントバインドされているため上記の jQuery.event.special.mouseleave.handler がフックされたまま残ってしまうからだ。

$("#A").unbind("mouseenter").unbind("mouseleave");

とするのが正しい。