イベントフェーズについて その1では、3段階あるイベントフェーズの中で 「ターゲット段階」・「バブリング段階」 に着目し、その仕組みをサンプルアプリの結果を見ながら確認しました。
今回は、残りのイベントフェーズ 「キャプチャ段階」について、私が勘違いしてしまったポイントも交えながら確認していきたいと思います。
まずは、キャプチャ段階のイベントをリスナーでキャッチさせる為に addEventListener メソッドの3番目の引数である useCapture を true に設定します。
(useCapture はデフォルトでは false に設定されています。つまり、ここのスイッチを変えない限り通常のイベントリスナーではキャプチャ段階は無視されています)
Flex3 API はこんな感じに書かれています。
今回のサンプルアプリではキャプチャ段階もリスナー対象としたいので、前回サンプルコードのinnerBox・middleBox・outerBox に登録したイベントリスナーの useCapture を true にします。
private function onCreationComplete():void{
innerBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher, true);
middleBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher, true);
outerBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher, true);
}
これでキャプチャ段階の設定がわかったので、前回サンプリコードの上記部分だけを変更して 実行すると 「キャプチャ段階」・「ターゲット段階」・「バブリング段階」 のすべてでイベントをキャッチできる! ・・・・・と思っていましたが、そうはいきませんでした。
実際の実行結果はこれです。
・・・いくらクリックしてもキャプチャ段階のイベントしかキャッチできていません。
Flex3 API の続きを読んでみるとちゃんと書いてありました。以下はAPI 説明からの引用です。
「useCapture を true に設定すると、リスナーはキャプチャ段階のみでイベントを処理し、ターゲット段階またはバブリング段階では処理しません。 useCapture を false に設定すると、リスナーはターゲット段階またはバブリング段階のみでイベントを処理します。 3 つの段階すべてでイベントを受け取るには、addEventListener() を 2 回呼び出します。useCapture を true に設定して 1 回呼び出し、useCapture を false に設定してもう 1 回呼び出します。」
ポイントとしては、イベントフェーズは3段階存在しているけれども、実際にはキャプチャー段階 と ターゲット段階+バブリング段階 の2種類でリスナー処理を登録してやらないといけないこと。 つまり3段階すべてでリスナー処理をさせるには、useCapture スイッチを切り替えた2つの addEventListener を発行してやらなければいけないみたいです。 なるほど。
以下が、私の勘違いを正した今回のサンプルアプリ用のコードです。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" horizontalAlign="center" backgroundColor="#FFFFFF"
creationComplete="onCreationComplete()" >
<mx:Script>
<![CDATA[
import mx.controls.Label;
private var counter:int = 1;
private function onCreationComplete():void{
innerBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher);
innerBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher,true);
middleBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher);
middleBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher, true);
outerBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher);
outerBox.addEventListener(MouseEvent.CLICK, onCatchDispatcher, true);
}
private function onCatchDispatcher(e:MouseEvent):void{
var lbl:Label = new Label();
var phase:String = getPhase(e.eventPhase);
lbl.text = counter + " : " + e.currentTarget.id + " [" + phase + "]";
setLabelColor(lbl, e.currentTarget.id);
eventList.addChild(lbl);
counter++;
}
private function getPhase(eventPhase:int):String{
var phase:String;
//EventPhase 1=キャプチャ段階, 2=ターゲット段階, 3=バブリング段階
switch(eventPhase){
case 1:
phase = "キャプチャ段階";
break;
case 2:
phase = "ターゲット段階";
break;
case 3:
phase = "バブリング段階";
break;
}
return phase;
}
private function setLabelColor(lbl:Label, targetBox:String):void{
if(targetBox == "innerBox"){
lbl.setStyle("color", "#CC0000");
}else if(targetBox == "middleBox"){
lbl.setStyle("color", "#00cc00");
}else if(targetBox == "outerBox"){
lbl.setStyle("color", "#0000CC");
}
}
]]>
</mx:Script>
<mx:HBox>
<mx:VBox id="outerBox" minHeight="150" minWidth="150" horizontalAlign="center" verticalAlign="middle" backgroundColor="#0000CC">
<mx:VBox id="middleBox" minHeight="100" minWidth="100" horizontalAlign="center" verticalAlign="middle" backgroundColor="#00CC00">
<mx:VBox id="innerBox" minHeight="50" minWidth="50" horizontalAlign="center" verticalAlign="middle" backgroundColor="#CC0000" />
</mx:VBox>
</mx:VBox>
<mx:VBox id="eventList" fontWeight="bold" />
</mx:HBox>
</mx:Application>
実際にサンプルアプリを動かしてみるとちゃんと3段階のイベントフェーズを確認することができました。
① 一番内側の innerBox をクリックした場合
② 次に内側の middleBox をクリックした場合
③ 一番外側の outerBox をクリックした場合
イベント伝播の機能はとても便利ですが、ここには伝播して欲しくないないんだよなーという場合もあると思います。
次回は伝播の止め方について確認していきたいと思います。