When events are triggered, there are three phases in which
Flex checks whether there are event listeners. These phases occur
in the following order:
Capturing
Targeting
Bubbling
During each of these phases, the nodes have a chance to react
to the event. For example, assume the user clicks a Button control
that is inside a VBox container. During the capturing phase, Flex
checks the Application object and the VBox for listeners to handle
the event. Flex then triggers the Button’s listeners in the target phase.
In the bubbling phase, the VBox and then the Application are again
given a chance to handle the event but now in the reverse order
from the order in which they were checked in the capturing phase.
In ActionScript 3.0, you can register event listeners on a target
node and on any node along the event flow. Not all events, however,
participate in all three phases of the event flow. Some types of
events are dispatched directly to the target node and participate
in neither the capturing nor the bubbling phases. All events can be
captured unless they are dispatched from the top node.
Other events may target objects that are not on the display list,
such as events dispatched to an instance of the Socket class. These
event objects flow directly to the target node, without participating
in the capturing or bubbling phases. You can also cancel an event
as it flows through the event model so that even though it was supposed
to continue to the other phases, you stopped it from doing so. You
can do this only if the cancelable property is
set to true.
Capturing and bubbling happen as the Event object moves from
node to node in the display list: parent-to-child for capturing
and child-to-parent for bubbling. This process has nothing to do
with the inheritance hierarchy. Only DisplayObject objects
(visual objects such as containers and controls) can have a capturing
phase and a bubbling phase in addition to the targeting phase.
Mouse events and keyboard events are among those that bubble.
Any event can be captured, but no DisplayObject objects listen during
the capturing phase unless you explicitly instruct them to do so.
In other words, capturing is disabled by default.
When a faceless event dispatcher, such as a Validator, dispatches
an event, there is only a targeting phase, because there is no visual
display list for the Event object to capture or bubble through.
About the target and currentTarget properties
Every Event object
has a target and a currentTarget property
that help you to keep track of where it is in the process of propagation.
The target property refers to the dispatcher of
the event. The currentTarget property refers to
the current node that is being examined for event listeners.
When you handle a mouse event such as MouseEvent.CLICK by
writing a listener on some component, the event.target property
does not necessarily refer to that component; it is often a subcomponent,
such as the Button control’s UITextField, that defines the label.
When Flash Player or Adobe® AIR™ dispatches an event, it dispatches the
event from the frontmost object under the mouse. Because children
are in front of parents, that means the player or AIR might dispatch
the event from an internal subcomponent, such as the UITextField
of a Button.
The event.target property is set to the object
that dispatched the event (in this case, UITextField), not the object
that is being listened to (in most cases, you have a Button control
listen for a click event).
MouseEvent events bubble up the parent chain, and can be handled
on any ancestor. As the event bubbles, the value of the event.target property
stays the same (UITextField), but the value of the event.currentTarget property is
set at each level to be the ancestor that is handling the event.
Eventually, the currentTarget will be Button, at
which time the Button control’s event listener will handle the event.
For this reason, you should use the event.currentTarget property
rather than the event.target property; for example:
In this case, in the Button event’s click event listener, the event.currentTarget property
always refers to the Button, while event.target might
be either the Button or its UITextField, depending on where on the
Button control the user clicked.
Capturing phase
In the capturing phase, Flex examines an event target’s
ancestors in the display list to see which ones are registered as
a listener for the event. Flex starts with the root ancestor and
continues down the display list to the direct ancestor of the target.
In most cases, the root ancestors are the Stage, then the SystemManager, and
then the Application object.
For example, if you have an application with a Panel container
that contains a TitleWindow container, which in turn contains a
Button control, the structure appears as follows:
Application
Panel
TitleWindow
Button
If your listener is on the click event of the
Button control, the following steps occur during the capturing phase
if capturing is enabled:
Check the Application container for click event listeners.
Check the Panel container for click event listeners.
Check the TitleWindow container for click event listeners.
During the capturing phase, Flex changes the value of the currentTarget property
on the Event object to match the current node whose listener is
being called. The target property continues to
refer to the dispatcher of the event.
By default, no container listens during the capturing phase.
The default value of the use_capture argument is false.
The only way to add a listener during this phase is to pass true for
the use_capture argument when calling the addEventListener() method,
as the following example shows:
If you add an event listener inline with MXML, Flex sets this
argument to false; you cannot override it.
If you set the use_capture argument to true—in
other words, if an event is propagated through the capturing phase—the
event can still bubble, but capture phase listeners will not react
to it. If you want your event to traverse both the capturing and
bubbling phases, you must call addEventListener() twice:
once with use_capture set to true, and then
again with use_capture set to false.
The capturing phase is very rarely used, and it can also be computationally intensive.
By contrast, bubbling is much more common.
Targeting phase
In the targeting phase, Flex invokes the event dispatcher’s
listeners. No other nodes on the display list are examined for event
listeners. The values of the currentTarget and
the target properties on the Event object during
the targeting phase are the same.
Bubbling phase
In the bubbling phase, Flex examines an event’s ancestors
for event listeners. Flex starts with the dispatcher’s immediate
ancestor and continues up the display list to the root ancestor.
This is the reverse of the capturing phase.
For example, if you have an application with a Panel container
that contains a TitleWindow container that contains a Button control,
the structure appears as follows:
Application
Panel
TitleWindow
Button
If your listener is on the click event of the
Button control, the following steps occur during the bubble phase
if bubbling is enabled:
Check the TitleWindow container for click event listeners.
Check the Panel container for click event listeners.
Check the Application container for click event listeners.
An event only bubbles if its bubbles property
is set to true. Mouse events and keyboard events
are among those that bubble; it is less common for higher-level events
that are dispatched by Flex to bubble. Events that can be bubbled
include change, click, doubleClick, keyDown, keyUp, mouseDown,
and mouseUp. To determine whether an event bubbles,
see the event’s entry in the ActionScript 3.0 Reference for the Adobe
Flash Platform.
During the bubbling phase, Flex changes the value of the currentTarget property
on the Event object to match the current node whose listener is
being called. The target property continues to
refer to the dispatcher of the event.
When Flex invokes an event listener, the Event object might have
actually been dispatched by an object deeper in the display list.
The object that originally dispatched the event is the target.
The object that the event is currently bubbling through is the currentTarget.
So, you should generally use the currentTarget property
instead of the target property when referring to the
current object in your event listeners.
If you set the useCapture property to true—in
other words, if an event is propagated through the capturing phase—then
it does not bubble, regardless of its default bubbling behavior.
If you want your event to traverse both the capturing and bubbling
phases, you must call addEventListener() twice: once
with useCapture set to true, and
then again with useCapture set to false.
An event only bubbles up the parent’s chain of ancestors in the
display list. Siblings, such as two Button controls inside the same
container, do not intercept each other’s events.
Detecting the event phase
You can determine what phase you are in by using the Event
object’s eventPhase property. This property contains
an integer that represents one of the following constants:
1 — Capturing phase (CAPTURING_PHASE)
2 — Targeting phase (AT_TARGET)
3 — Bubbling phase (BUBBLING_PHASE)
The following example displays the current phase and information
about the current target:
You can call either the event’s stopPropagation() method
or the stopImmediatePropagation() method to prevent
an Event object from continuing on its way through the event flow.
The two methods are nearly identical and differ only in whether
the current node’s remaining event listeners are allowed to execute.
The stopPropagation() method prevents the Event object
from moving on to the next node, but only after any other event
listeners on the current node are allowed to execute.
The stopImmediatePropagation() method also prevents
the Event objects from moving on to the next node, but it does not
allow any other event listeners on the current node to execute.
The following example creates a TitleWindow container
inside a Panel container. Both containers are registered to listen
for a mouseDown event. As a result, if you click
on the TitleWindow container, the showAlert() method
is called twice unless you add a call to the stopImmediatePropagation() method,
as the following example shows:
The executing SWF file for the previous example is shown below:
Note: A call to either the Event.stopPropogation() or
the Event.stopImmediatePropogation() methods does
not prevent default behavior from occurring.
Event examples
In the following example, the parent container’s click
handler disables the target control after the target handles the
event. It shows that you can reuse the logic of a single listener
(click the HGroup container) for multiple events (all the clicks).
<?xml version="1.0"?>
<!-- events/NestedHandlers.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script><![CDATA[
public function disableControl(event:MouseEvent):void {
// Use this same logic for all events.
event.currentTarget.enabled = false;
}
public function doSomething(event:MouseEvent):void {
b1.label = "clicked";
ta1.text += "Something happened.";
}
public function doSomethingElse(event:MouseEvent):void {
b2.label = "clicked";
ta1.text += "Something happened again.";
}
]]></fx:Script>
<s:HGroup id="hb1" height="200" click="disableControl(event)">
<s:Button id='b1' label="Click Me" click="doSomething(event)"/>
<s:Button id='b2' label="Click Me" click="doSomethingElse(event)"/>
<s:TextArea id="ta1"/>
</s:HGroup>
<s:Button id="resetButton"
label="Reset"
click="hb1.enabled=true;b1.enabled=true;b2.enabled=true;b1.label='Click Me';b2.label='Click Me';"/>
</s:Application>
The executing SWF file for the previous example is shown below:
By having a single listener on a parent control instead of many
listeners (one on each child control), you can reduce your code
size and make your applications more efficient. Reducing the number
of calls to the addEventListener() method potentially
reduces application startup time and memory usage.
The following example registers an event handler for the Panel
container, rather than registering a listener for each link. All
children of the Panel container inherit this event handler. Since
Flex invokes the handler on a bubbled event, you use the target property
rather than the currentTarget property. In this handler,
the currentTarget property would refer to the Panel
control, whereas the target property refers to
the LinkButton control, which has the label that you want.
<?xml version="1.0"?>
<!-- events/SingleRegisterHandler.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="createLinkHandler();">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
private function linkHandler(event:MouseEvent):void {
try {
var url:URLRequest = new URLRequest("http://finance.google.com/finance?q=" +
event.target.label);
navigateToURL(url);
} catch (e:Error) {
/**
* Do nothing; just want to catch the error that occurs when a user clicks on
* the Panel and not one of the LinkButtons.
**/
}
}
private function createLinkHandler():void {
p1.addEventListener(MouseEvent.CLICK,linkHandler);
}
]]>
</fx:Script>
<s:Panel id="p1" title="Click on a stock ticker symbol">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<mx:LinkButton label="ADBE"/>
<mx:LinkButton label="GE"/>
<mx:LinkButton label="IBM"/>
<mx:LinkButton label="INTC"/>
</s:Panel>
</s:Application>
The executing SWF file for the previous example is shown below: