uhyohyo.net

JavaScript初級者から中級者になろう

三章第六回 mouseoveerとmouseout

今回は、mouseoverとmouseoutという2つのイベントについて詳しく解説します。

<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      p{
        background-color:yellow;
        width:300px;
        height:300px;
      }
    </style>
  </head>
  <body>

    <p onmouseover="console.log('mouseover');" onmouseout="console.log('mouseout');">test</p>

  </body>
</html>

黄色い部分がp要素で、これにはmouseoverとmouseoutのイベントが設定されています。マウスがp要素の外からp要素の上にはいるとmouseover、p要素の上からp要素の外にでるとmouseoutが発生しているのがわかります。つまり、mouseoverとmouseoutというイベントは、ある要素の上にマウスが入ったときと出たときにそれぞれ発生するのです。ある部分にマウスが移動するとなにかが動くというような動作は、今でこそCSS3を用いて作られることが多いですが、一昔前はJavaScriptのこれらのイベントを使って実装されていました。

さて、ここで、body要素に注目します。body要素には、特にイベントが設定されていませんね。ここで、黄色い部分はp要素ですが、それ以外の部分はbody要素です。p要素要素はbody要素の子ではあるものの、一応body要素とはまた別の領域です。

つまり、どういうことかというと、body要素からp要素の上にマウスが移動するとき、body要素から見ると、p要素という別の領域にマウスがいってしまうことだから、body要素でmouseoutが同時に発生するということです。

同様に、p要素の上からbody要素にマウスが移動するとき、p要素ではmouseoutが発生しますが、body要素では、別の領域からbody要素の上にマウスが戻ってきたことになるから、body要素ではmouseoverが同時に発生するということです。

このように、mouseoverとmouseoutは対になって発生します。さらに、三章第三回で解説したイベントバブリングの考え方を加えると、p要素で発生したmouseoverやmouseoutは、body要素にも伝わります(あくまでイベントが発生したのはp要素で、body要素で実際に起こったというわけではなく、あくまで伝わるだけです)。

どういうことかというと、body要素では、mouseoverとmouseoutが同時に処理されるということです(ただし厳密には2つのプログラムを同時に実行できませんので、順番に処理されますが、そのタイミングは同じです)。

特に、前回みたように画面上のものは全てdocumentの下にあるから、どことどこの要素の間をマウスが移動しても、mouseoverとmouseoutはdocumentに伝わり、documentではmouseoverとmouseoutが同時に処理されます。

つまり、mouseoverとmouseoutは本質的に同じイベントであるということができます。

そこで、DOMでは、この2つを統一的に、まとめて扱う方法が用意されています。

<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      p{
        background-color:yellow;
        width:300px;
        height:300px;
      }
    </style>
  </head>
  <body>

    <p>test</p>

    <script type="text/javascript">
      var listener = function(ev){
          console.log("target : "+ev.target.tagName,"relatedTarget : "+ev.relatedTarget.tagName);
      };

      document.addEventListener('mouseover' , listener,false);
    </script>
  </body>
</html>

今回は、mouseoverにイベントを登録しています。documentに登録しているということは、前回解説したように、この文書上のいかなる場所で起こったmouseoverも全て感知するということです。

さて、イベントリスナでは、イベントオブジェクトの持つプロパティtargetと、relatedTargetのtagNameを表示しています。targetは前回解説した通り、そのイベントが実際に起こった要素ですね。

今回新しく登場したのが、このrelatedTargetです。tagNameというプロパティを持っていることから、これもHTMLElementであることが分かります。

このrelatedTargetは、感知したイベントと対になるイベントが起こった要素を表しています。例えば、body要素からp要素にマウスを動かしたとき、p要素でmouseoverが発生したから、targetは当然p要素です。このとき、マウスがbody要素の上から離れたことで、対になるmouseoutがbody要素で発生します。このbody要素がrelatedTargetになります。

実際にbody要素からp要素にマウスを動かしてみると、target.tagNameがPで、relatedTarget.tagNameがBODYであることが分かります。

もっと分かりやすく述べるならば、mouseoverの場合、マウスが移動した先の要素がtarget、移動元がrelatedTargetです。mouseoutは逆に、移動元がtarget、移動先がrelatedTargetです。

ついでに、いろいろ試していると、「P」「BODY」だけでなく「HTML」が出てくることがあるのに気づくかもしれません。実は、画面上にはbody要素ではない領域もあって、その部分はhtml要素が司っています。