アプリ版:「スタンプのみでお礼する」機能のリリースについて

http://oshiete.goo.ne.jp/qa/6681276.html
に関連しますが


divの中に、p、ulが同列にあり
div領域からmouseoutしたらイベントが起きるようにしたいと思っています。

イベントは親要素に伝播するということなので
event.cancelBubble = true;
window.event.cancelBubble=true;
をp,ulのイベントに入れました。

又、各要素のマウス状態の履歴をテーブルに残してみました(下にソース)。
(マウスアウトしたら「x」、オーバーで「o」)



その結果、

バブリング防止策を講じたため
pまたはulのマウスアウトはdivに伝わっていません(ただしFirefoxではダメ?)。

ところがよく見ると
div→p
div→ul
と移動する際、div内ではありますが
divは一旦マウスアウトしてから再びマウスオーバーとなっているようです。


まず、ここまでの理解は間違っていないでしょうか?
そして、境界線をまたぐ際、マウスアウトになるのはどうにもならないのでしょうか?


基本的な質問で申し訳ありませんが、よろしくお願いします。

---------------------------------------------------------------------------------------------

<script style="text/javascript">
function divOn() {
document.getElementById('atDiv').innerHTML=document.getElementById('atDiv').innerHTML+'o';
}
function divOff() {
document.getElementById('atDiv').innerHTML=document.getElementById('atDiv').innerHTML+'x';
}
function pOn() {
document.getElementById('atP').innerHTML=document.getElementById('atP').innerHTML+'o';
}
function pOff() {
document.getElementById('atP').innerHTML=document.getElementById('atP').innerHTML+'x';
}
function ulOn() {
document.getElementById('atUl').innerHTML=document.getElementById('atUl').innerHTML+'o';
}
function ulOff() {
document.getElementById('atUl').innerHTML=document.getElementById('atUl').innerHTML+'x';
}
function stopBubbling() {
event.cancelBubble=true;
stopPropagation=true;
}
</script>


<div onMouseover="divOn();" onMouseout="divOff();" style="background:#fee;padding:1em;height:300px;">div
<p onMouseover="pOn();" onMouseout="stopBubbling();pOff();" style="background:#efe;height:100px;">p</p>
<ul onMouseover="ulOn();" onMouseout="stopBubbling();ulOff();" style=";background:#eef;height:100px;">
<li><a>li</a></li>
<li><a>li</a></li>
</ul>
</div>


<table border="1">
<thead>
<tr>
<th>div</th>
<th>p</th>
<th>ul</th>
</tr>
</thead>
<tbody>
<tr>
<td id="atDiv"><br /></td>
<td id="atP"><br/></td>
<td id="atUl"><br/></td>
</tr>
</tbody>
</table>

A 回答 (4件)

バブリング(イベントフロー)を勝手に止めてはいけません。

かつ、イベントフローを停止する正規の手段は event.stopPropagation() であり、event.cancelBubble は(もともと)IE の独自仕様です。

マウスオーバーとマウスアウトは、どちらをイベントの発生源とするかという視点の違いに過ぎず、本質的に同じイベントです。

・event.target でマウスオーバーが生じたら、event.relatedTarget にはマウスアウトした要素がセットされる
・event.target でマウスアウトが生じたら、event.relatedTarget にはマウスオーバーした要素がセットされる
・IE8 以下の場合、event.fromElement にマウスアウトした要素、event.toElement にマウスオーバーした要素がセットされる

したがって、どちらか一方を document で監視すれば事足りるのです。

// document で mouseover を監視すれば
function onMouseOver (event) {
 // document 内で生じる mouseover と mouseout の両方を全て感知できる
 var over = event.target || event.toElement;
 var out = event.relatedTarget || event.fromElement;
 var tmp;
 
 if (out) {
  // ただしウィンドウ外へ出ていった場合は感知できない
  for (tmp = out; tmp; tmp = tmp.parentNode) {
   if (tmp.id === 'aDiv') break;
  }
  if (tmp) {
   // #aDiv の外に出ていった
  }
 }
 for (tmp = over; tmp; tmp = tmp.parentNode) {
  if (tmp.id === 'aDiv') break;
 }
 if (tmp) {
  // #aDiv の中に入った
 }
 // Selectors API2 を使えばもう少し分かりやすいが割愛
}

もし、どうしても div 自体に onmouseout 属性を書いて「div から出ていった」ことを感知したい場合は、マウスの行き先(mouseout なら event.relatedTarget または event.toElement で分かる)が、div の中にないことをテストすれば良い。この辺は Dreamweaver の吐き出すコードでも昔からやっていることです。

<div onmouseout="
 if (mouseIsOutside.call(this, event)) {
  // div から出ていった
 }
">...</div>

function mouseIsOutside (event) {
 var r;
 var c;
 try { // DOM3 Core (Firefox, Opera10, Safari4)
  c = event.currentTarget;
  r = event.relatedTarget;
  return ! r || ! r.isSameNode(c) && 0 === (r.compareDocumentPosition(c) & Node.DOCUMENT_POSITION_CONTAINS);
 }
 catch (err) {
  try { // MSHMTL (IE, Opera9)
   c = this;
   r = event.toElement || event.relatedTarget;
   return ! r || c !== r && ! c.contains(r);
  }
  catch (err) { // DOM1 Core (Safari3)
   c = event.currentTarget;
   r = event.relatedTarget;
   while ((r = r.parentNode)) if (r === c) break;
   return ! r;
  }
 }
}

バブリングしない mouseenter、mouseleave(IE 由来で DOM3 Events に収録予定)を使っても良いのですが、いずれにせよバブリングを使えるなら document で一括監視でき、load やノードの挿入を監視してイベントハンドラを付け足す……のような変な作業をせずに済みます。

この回答への補足

詳しいご回答ありがとうございます。
documentで監視するというのがよさそうですね。
いろいろ試してみます。

補足日時:2011/04/26 11:21
    • good
    • 0
この回答へのお礼

返答ありがとうございます!
今、別の要件が入ったので、後で改めてお礼を述べさせていただきます。

お礼日時:2011/04/22 11:59

すみません、訂正します。



>onmouseoutのかわりに、onmouseleaveというフックがあります。

フックではなくハンドラです。
(フックは別の言語、APIの呼び方でした)

この回答への補足

ありがとうございます。
onmouseleaveというのは初めて知りました。
使えそうですね。

補足日時:2011/04/26 11:19
    • good
    • 0
この回答へのお礼

返答ありがとうございます!
今、別の要件が入ったので、後で改めてお礼を述べさせていただきます。

お礼日時:2011/04/22 11:59

>まず、ここまでの理解は間違っていないでしょうか?


その通りです。
ツールチップやプルダウンメニューなどを表示・非表示させる場合によくある問題です。


>そして、境界線をまたぐ際、マウスアウトになるのはどうにもならないのでしょうか?
その問題に対応するために、
onmouseoutのかわりに、onmouseleaveというフックがあります。

onmouseleaveに対応していないブラウザでは、
私はevent.target、event.srcElementからparentNodeを調べていき、
onmouseoutをセットした要素と同じ物があればmouseoutしていないという判断、
同じ要素がなければ完全に外に出たとして、mouseout処理を続行
としています。

内側の要素でさらにonmouseoverやonmouseoutイベントを処理している場合で、イベントバブルを停止させている場合は、
target|srcElementがうまく取得できないこともあるので、
タイマーを使って1ミリ秒後にカーソルの位置を判断する、という方法をよく使っています。

この回答への補足

ありがとうございます。
「タイマーを使って1ミリ秒後にカーソルの位置を判断する」というのは考えました。
色々試してみます!

補足日時:2011/04/26 11:19
    • good
    • 0
この回答へのお礼

返答ありがとうございます!
今、別の要件が入ったので、後で改めてお礼を述べさせていただきます。

お礼日時:2011/04/22 11:59

この回答への補足

ありがとうございます!
基本的なことが詳しく書かれていますね。
色々試してみたいと思います。

補足日時:2011/04/26 11:17
    • good
    • 0
この回答へのお礼

返答ありがとうございます!
今、別の要件が入ったので、後で改めてお礼を述べさせていただきます。

お礼日時:2011/04/22 11:58

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!