Event伝搬を理解する(JP)
ネスケを恨んだ制作現場
AS3の理解は大切になってきている。過去web制作を経験したものなら、IEやネスケ、特にネスケには様々なバージョンがあったことを知っている。今でこそfirefoxが主流となりつつありIEが悪者扱いされているが、当時はネスケの動作を正常にするためには大幅な作業時間を割かざる得なかった。クロスプラットフォームのウェブサイトをjavascriptでインタラクティブな動作も加えてユーザに見せたいというクライアントの欲求は当時からあった。当時自分は
永谷園のサイトでフレームを使わないでフレームを使っているようにメニューが切り替わるものを制作した。フレームを使えば押されたボタンのメニューページを何十ページも制作しなければならない。しかしフレーム無しでDHTMLがあれば、メニューが1ページで制作が済む。当時からオブジェクト指向のjavascriptでメニューの制作をしていたので、コードがコンパクトで見やすかったものの、一番苦労したのはブラウザの差を消す部分。制作現場で主流だったmacのIE4.5や古いネスケでいかに同じように見えるかということには結果的に一番時間をかけてしまった。ネスケの話はActionscriptでも同じことが言えないだろうか。一部のネスケ愛好家やOS9の根強いユーザがやはりウェブ制作現場の新しい技術へ移行しにくくしていたことは否定できない。古いバージョンのアクションスクリプトをいつまでも使い続けることは少数派利用のブラウザや古いOSのユーザを恨み妬んだ制作者にはあってはならないことと感じる。
イベントとは何か
AS3ではイベントの伝搬の仕組みが変更されたので今までのアクションスクリプトのバージョンのマウスの動作がそのまま再現されるとは限らない。例えばonReleaseOutsideというものは存在しない。そもそもイベントとは何かを理解していく。
簡単に理解できるようにマウスを例に挙げると、マウスのボタンが押されたとき、離されたとき、ホイールを転がしたときにflashはある関数を自動的に呼び出している。この関数にある処理をすることでマウスで何かが起きたときに制作者がしたい動作をアクションスクリプトで記述することができる。この時、制作者がしたい動作がイベントハンドラメソッドであり、そのイベントハンドラを呼び出す登録がaddEventListenerと理解できる。
function onMouseClick(e:MouseEvent):void{ //マウスを押したらしてほしい動作 }
button.addEventListener(MouseEvent.CLICK, onMouseClick);//buttonで起きたクリックでonMouseClickという動作をする登録処理
イベントの伝搬
buttonでクリックが起こった場合、buttonのみがクリックが起きたことを知るわけではない。確かにbuttonをクリックすればクリックが起きたのはbuttonだが、最上位の親からbuttonに至るまでの通るすべてのobjectもそのクリックを知っている。ただし、それを知るのは親もリスナー登録した場合に限られる。
この例ではstageがリスナー登録をしているので、buttonがクリックされるとstageにもクリックが知らされる。その場合クリックが知らされるのは、buttonにeventが知らされた後である。イベントの伝搬にはcapture、event、bubblingの3つのフェーズがある。AS2までで意識していたのはeventのフェーズのみなので、少し分かりづらいかもしれない。
button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理 button.addEventListener(MouseEvent.CLICK, buttonClick2);//event処理 stage.addEventListener(MouseEvent.CLICK, stageClick1);//bubbling処理 function buttonClick1(e){ trace("click1"); } function buttonClick2(e){ trace("click2"); } function stageClick1(e){ trace("stage1"); } //---- click1 click2 stage1
親に先に知らせたい
buttonに知らされる前にstageに知らせたい場合もあるかもしれない。その場合はuseCaptureという機能を使う。useCaptureを記述しない又はfalseの場合は、eventとbubblingのフェーズのみが通知される。
button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理 button.addEventListener(MouseEvent.CLICK, buttonClick2);//event処理 stage.addEventListener(MouseEvent.CLICK, stageClick1,true);//capture処理 stage.addEventListener(MouseEvent.CLICK, stageClick2);//bubbling処理 function buttonClick1(e){ trace("click1"); } function buttonClick2(e){ trace("click2"); } function stageClick1(e){ trace("stage1"); } function stageClick2(e){ trace("stage2"); } //---- stage1 click1 click2 stage2
stageをクリックしたらどうなるか
stageをクリックすると、子へ伝える必要が無いので、stage止まりになる。前節のとおり、false又は省いた場合はeventとbubblingを処理する。ここではstageでクリックというeventが発生しているのでstageClick2イベントハンドラで処理される。capturingは子でeventが起きた場合に起こるのでここでは呼び出されない。
button.addEventListener(MouseEvent.CLICK, buttonClick1); button.addEventListener(MouseEvent.CLICK, buttonClick2); stage.addEventListener(MouseEvent.CLICK, stageClick1,true); stage.addEventListener(MouseEvent.CLICK, stageClick2);//event処理 function buttonClick1(e){ trace("click1"); } function buttonClick2(e){ trace("click2"); } function stageClick1(e){ trace("stage1"); } function stageClick2(e){ trace("stage2"); } //---- stage2
イベント伝搬を途中でやめる
子でクリックされた後に親にそのことを伝えたくない場合もある。stopPropagation()を使うとeventが発生している場所で処理が終わった後の伝搬が停止する。この例ではbuttonでeventが起きているので、buttonClick1とbuttonClick2が処理されその後のstageClick2が行われない。
button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理(+stopPropagation) button.addEventListener(MouseEvent.CLICK, buttonClick2);//event処理 stage.addEventListener(MouseEvent.CLICK, stageClick1,true);//capture処理 stage.addEventListener(MouseEvent.CLICK, stageClick2); function buttonClick1(e){ e.stopPropagation(); trace("click1"); } function buttonClick2(e){ trace("click2"); } function stageClick1(e){ trace("stage1"); } function stageClick2(e){ trace("stage2"); } //---- stage1 click1 click2stopImmidiatePropagation()を使うと即座に伝搬が停止するので、この例ではbuttonClick1以降が実行されない。
button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理(+stopImmidiatePropagation) button.addEventListener(MouseEvent.CLICK, buttonClick2); stage.addEventListener(MouseEvent.CLICK, stageClick1,true);//capture処理 stage.addEventListener(MouseEvent.CLICK, stageClick2); function buttonClick1(e){ e.stopImmidiatePropagation(); trace("click1"); } function buttonClick2(e){ trace("click2"); } function stageClick1(e){ trace("stage1"); } function stageClick2(e){ trace("stage2"); } //---- stage1 click1
useCaptureのまとめ
stageに対してのリスナー登録でuseCaptureをtrue又はfalse(デフォルト)にしたときの動作をまとめると次のようになる。buttonでCLICKイベントが起きたとき、stageのuseCaptureがtrueであれば、buttonでeventフェーズの処理が行われる前にstageのcaptureフェーズで呼ばれる。その後、buttonのeventが呼ばれ、stageのbubblingが呼ばれる。基本的にはbuttonのeventだけを処理すると考えるのが一般的だろう。useCaptureがfalseであれば、buttonのeventがまず最初に呼ばれ、その後にstageのbubblingと続く。一方stageをクリックした場合もCLICKイベントが呼ばれるが、stageのeventであるので、captureとbubblingは行われない。eventを処理するのはfalse(デフォルト)の場合のみとなる。