event handling って難しい

JavaScriptのイベントハンドラに頭を悩ます。。

を見てさらに突っ込んでみた。ここでは便宜的にイベントハンドリングには以下の3つの方法があるものとする。

  1. HTML attribute
    • <a> とか
  2. DOM event listener
    • obj.addEventListener(), obj.attachEvent()
  3. DOM 0 property
    • obj.onlick = func とか

これを上の優先順位で付加した。実際には DOM 0 property は try catch で event listener のセットに失敗したときだけ試すことにした。

結果、意外なことが分かった。

手元の環境では

  • DOM event が優先
    • Gecko ブラウザのみ
  • HTML attribute が優先
    • WinIE 6, Opera 9.23, Safari 1.3, iCab 3.03

合わせ技で、HTML attribute と DOM event listener の両方で return false した場合、

  1. HTML attribute
  2. DOM event listener
  3. 停止
    • WinIE は DOM event listener の return false で停止
    • Safari 1.3, Opera 9.23, iCab 3.03 は HTML attribute の return false で停止(つまり event listener だけで return false しても止まらないでページ遷移してしまう。)

という流れになる。

ちなみに Firefox で同じことをすると DOM event listener が先に実行されて、その return false で停止する。

ただなんか Safari はよく分からなくて、手元の Safari 1.3 では

  • a タグは DOM event listener が優先
  • form タグは HTML attribute が優先

てな動きに。なんだかよく分からん。本当に一貫してこの通りの動きになるのかは分かりません。

ちなみに、どのブラウザでも DOM 0 property にセットしたものは HTML attribute を上書きする形で動くらしく、この中での return false によってページ遷移を抑止することができた。逆に言うと、onsubmit で submit の無効化とかしたい場合は、現状では DOM event listener は役に立たず、DOM 0 property の方にセットしないとダメっぽいです。 DOM 0 property の方にセットして false を返すか、DOM event listener にセットして event オブジェクトを通じて動作の停止を行うかのいずれか。

複数のイベントハンドラをセットする必要が今のところ自分にはないのでこれ以上細かくは調べてないけど、ページ遷移抑止をしつつ複数のイベントハンドラを a や form で動かそうと思ったら、どうするのが正解なんでしょうな event オブジェクトをちゃんと使えってことですな。

[2007-09-15 追記]

分かりました。以下、「イベントハンドラ」は DOM event listener の意味で用います。

  • イベントハンドラの return を見てくれるのは IE だけ
  • イベントハンドラからページ遷移を抑止するためには event オブジェクトに対してそのように指示を出さないとダメ
    • 明示の方法は event モデルに従う
  • イベントハンドラは黙ってても「イベントオブジェクト」を受け取る
    • 仕様になってるわけじゃないけど、そういう実装になってる
  • attachEvent() で動作する IE, Opera, iCab は window.event オブジェクトをいきなり参照できる
  • addEventListener() で動作する Firefox, Safari は明示的にイベントハンドラの方で引数を指定しておいて event オブジェクトを受け取る
    • かと思いきや Safari はなぜかイベントハンドラ function で引数を明示しておかなくても `event' リテラルが「W3C モデルの event オブジェクト」を意味してまともに動いちゃう
    • しかも addEventListener() で第3引数を省いても動くなどいい具合にアバウト

すっげ。Safari すっげ。どういうことかというと、こんなんでも動くってことです。

obj.addEventListener( 'click', function() {
  alert( event.target );
} );

Firefox だと event なんてオブジェクトは function() 内に存在しないので動きません。ふーむ。

登場人物は Opera 9.23, iCab 3.03, Safari 1.3(一部2), Firefox 2.0.7, WinIE 6 とワタクシ wtnabe、special thanks to odz ! でお送りしました。

More