One of the most important parts of your Flex application is handling events by using controls and ActionScript.
Events let a developer know when something happens within an application. They can be generated by user devices, such as the mouse and keyboard, or other external input, such as the return of a web service call. Events are also triggered when changes happen in the appearance or life cycle of a component, such as the creation or destruction of a component or when the component is resized.
Any user interaction with your application can generate events. Events can also occur without any direct user interaction, such as when data finishes loading from a server or when an attached camera becomes active. You can "handle" these events in your code by adding an event handler. Event handlers are the functions or methods that you write to respond to specific events. They are also sometimes referred to as event listeners.
The Flex event model is based on the Document Object Model (DOM) Level 3 events model. Although Flex does not adhere specifically to the DOM standard, the implementations are very similar. The event model in Flex comprises the Event object and its subclasses, and the event dispatching model. For a quick start in using events in Flex, see the sample code in Using events.
Components generate and dispatch events and consume (listen to) other events. An object that requires information about another object's events registers a listener with that object. When an event occurs, the object dispatches the event to all registered listeners by calling a function that was requested during registration. To receive multiple events from the same object, you must register your listener for each event.
Components have built-in events that you can handle in ActionScript blocks in your MXML applications. You can also take advantage of the Flex event system's dispatcher-listener model to define your own event listeners outside of your applications, and define which methods of your custom listeners will listen to certain events. You can register listeners with the target object so that when the target object dispatches an event, the listeners get called.
All visual objects, including Flex controls and containers, are subclasses of the DisplayObject class. They are in a tree of visible objects that make up your application. The root of the tree is the Stage. Below that is the SystemManager object, and then the Application object. Child containers and components are leaf nodes of the tree. That tree is known as the display list. An object on the display list is analogous to a node in the DOM hierarchical structure. The terms display list object and node are used interchangeably.
For information about each component's events, see the component's description in UI Controls or the control's entry in ActionScript 3.0 Reference for Apache Flex.
For a detailed description of a component's startup life cycle, including major events in that life cycle, see Create advanced MX visual components in ActionScript.
You can instruct any container or control to listen for events dispatched by another container or control. When Adobe® Flash® Player dispatches an Event object, that Event object makes a roundtrip journey from the root of the display list to the target node, checking each node for registered listeners. The target node is the node in the display list where the event occurred. For example, if a user clicks a Button control named Child1, Flash Player dispatches an Event object with Child1 defined as the target node.
The event flow is conceptually divided into three parts: the capturing phase, the targeting phase, and the bubbling phase, as briefly described next. For more information about the event flow, see Event propagation.
The first part of the event flow is called the capturing phase. This phase comprises all of the nodes from the root node to the parent of the target node. During this phase, Flash Player examines each node, starting with the root, to see if it has a listener registered to handle the event. If it does, Flash Player sets the appropriate values of the Event object and then calls that listener. Flash Player stops after it reaches the target node's parent and calls any listeners registered on the parent. For more information, see Capturing phase.
The second part of the event flow, the targeting phase, consists solely of the target node. Flash Player sets the appropriate values on the Event object, checks the target node for registered event listeners, and then calls those listeners. For more information, see Targeting phase.
The third part of the event flow, the bubbling phase, comprises all of the nodes from the target node's parent to the root node. Starting with the target node's parent, Flash Player sets the appropriate values on the Event object and then calls event listeners on each of these nodes. Flash Player stops after calling any listeners on the root node. For more information about the bubbling phase, see Bubbling phase.
The Event class is an ActionScript class with properties that contain information about the event that occurred. An Event object is an implicitly created object, similar to the request and response objects in a JavaServer Page (JSP) that are implicitly created by the application server.
Flex creates an Event object each time an event is dispatched. You can use the Event object inside an event listener to access details about the event that was dispatched, or about the component that dispatched the event. Passing an Event object to, and using it in, an event listener is optional. However, if you want to access the Event object's properties inside your event listeners, you must pass the Event object to the listener.
Flex creates only one Event object when an event is dispatched. During the bubbling and capturing phases, Flex changes the values on the Event object as it moves up or down the display list, rather than creating a new Event object for each node.
There are many classes that extend the flash.events.Event class. These classes are defined mostly in the following packages:
spark.events.*
mx.events.*
flash.events.*
The mx.events package defines event classes that are specific to most Flex controls, including the DataGridEvent, DragEvent, and ColorPickerEvent. The spark.events package defines event classes that are specific to a few Spark controls, including the TextOperationEvent and VideoEvent. The flash.events package describes events that are not unique to Flex but are instead defined by Flash Player. These event classes include MouseEvent, DataEvent, and TextEvent. All of these events are commonly used in applications.
In addition to these packages, some packages also define their own event objects: for example, mx.messaging.events.ChannelEvent and mx.logging.LogEvent.
Child classes of the Event class have additional properties and methods that may be unique to them. In some cases, you will want to use a more specific event type rather than the generic Event object so that you can access these unique properties or methods. For example, the LogEvent class has a getLevelString() method that the Event class does not.
For information on using Event subclasses, see Using event subclasses.
Every object in the display list can trace its class inheritance back to the DisplayObject class. The DisplayObject class, in turn, inherits from the EventDispatcher class. The EventDispatcher class is a base class that provides important event model functionality for every object on the display list. Because the DisplayObject class inherits from the EventDispatcher class, any object on the display list has access to the methods of the EventDispatcher class.
This is significant because every item on the display list can participate fully in the event model. Every object on the display list can use its addEventListener() method—inherited from the EventDispatcher class—to listen for a particular event, but only if the listening object is part of the event flow for that event.
Although the name EventDispatcher seems to imply that this class's main purpose is to send (or dispatch) Event objects, the methods of this class are used much more frequently to register event listeners, check for event listeners, and remove event listeners.
The EventDispatcher class implements the IEventDispatcher interface. This allows developers who create custom classes that cannot inherit from EventDispatcher or one of its subclasses to implement the IEventDispatcher interface to gain access to its methods.
The addEventListener() method is the most commonly used method of this class. You use it to register your event listeners. For information on using the addEventListener() method, see Using the addEventListener() method.
Advanced programmers use the dispatchEvent() method to manually dispatch an event or to send a custom Event object into the event flow. For more information, see Manually dispatching events.
Several other methods of the EventDispatcher class provide useful information about the existence of event listeners. The hasEventListener() method returns true if an event listener is found for that specific event type on a particular display list object. The willTrigger() method checks for event listeners on a particular display list object, but it also checks for listeners on all of that display list object's ancestors for all phases of the event flow. The method returns true if it finds one.
Using events in Flex is a two-step process. First, you write a function or class method, known as an event listener or event handler, that responds to events. The function often accesses the properties of the Event object or some other settings of the application state. The signature of this function usually includes an argument that specifies the event type being passed in.
The following example shows a simple event listener function that reports when a control triggers the event that it is listening for:
<?xml version="1.0"?>
<!-- events/SimpleEventHandler.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="initApp();">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function initApp():void {
b1.addEventListener(MouseEvent.CLICK, myEventHandler);
}
private function myEventHandler(event:Event):void {
Alert.show("An event occurred.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
</s:Application>
As you can see in this example, you also register that function or class method with a display list object by using the addEventListener() method.
Most Flex controls simplify listener registration by letting you specify the listener inside the MXML tag. For example, instead of using the addEventListener() method to specify a listener function for the Button control's click event, you specify it in the click attribute of the <mx:Button> tag:
<?xml version="1.0"?>
<!-- events/SimplerEventHandler.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myEventHandler(event:Event):void {
Alert.show("An event occurred.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me" click="myEventHandler(event)"/>
</s:Application>
This is equivalent to the addEventListener() method in the previous code example. However, it is best practice to use the addEventListener() method. This method gives you greater control over the event by letting you configure the priority and capturing settings, and use event constants. In addition, if you use addEventListener() to add an event handler, you can use removeEventListener() to remove the handler when you no longer need it. If you add an event handler inline, you cannot call removeEventListener() on that handler.
Each time a control generates an event, Flex creates an Event object that contains information about that event, including the type of event and a reference to the dispatching control. To use the Event object, you specify it as a parameter in the event handler function, as the following example shows:
<?xml version="1.0"?>
<!-- events/EventTypeHandler.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myEventHandler(e:Event):void {
Alert.show("An event of type '" + e.type + "' occurred.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me" click="myEventHandler(event)"/>
</s:Application>
If you want to access the Event object in an event handler that was triggered by an inline event, you must add the event keyword inside the MXML tag so that Flex explicitly passes it to the handler, as in the following:
<mx:Button id="b1" label="Click Me" click="myEventHandler(event)"/>
You are not required to use the Event object in a handler function. The following example creates two event handler functions and registers them with the events of a ComboBox control. The first event handler, openEvt(), takes no arguments. The second event handler, changeEvt(), takes the Event object as an argument and uses this object to access the value and selectedIndex of the ComboBox control that triggered the event.
<?xml version="1.0"?>
<!-- events/MultipleEventHandlers.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[
private function openEvt():void {
forChange.text="";
}
private function changeEvt(e:Event):void {
forChange.text =
"Value: " + e.currentTarget.selectedItem + "\n" +
"Index: " + e.currentTarget.selectedIndex;
}
]]></fx:Script>
<s:ComboBox open="openEvt()" change="changeEvt(event)">
<s:dataProvider>
<s:ArrayList>
<fx:String>AK</fx:String>
<fx:String>AL</fx:String>
<fx:String>AR</fx:String>
</s:ArrayList>
</s:dataProvider>
</s:ComboBox>
<s:TextArea id="forChange" width="150" height="100"/>
</s:Application>
This example shows accessing the target property of the Event object. For more information, see Accessing the currentTarget property.
You specify the object in a listener function's signature as type Event, as the following example shows:
function myEventListener(e:Event):void { ... }
However, if you want to access properties that are specific to the type of event that was dispatched, you must instead specify a more specific event type, such as ToolTipEvent or KeyboardEvent, as the following example shows:
import mx.events.ToolTip
function myEventListener(e:ToolTipEvent):void { ... }
In some cases, you must import the event's class in your ActionScript block.
Most objects have specific events that are associated with them, and most of them can dispatch more than one type of event.
If you declare an event of type Event, you can cast it to a more specific type to access its event-specific properties. For more information, see Using event subclasses.
Event objects include a reference to the instance of the dispatching component (or target), which means that you can access all the properties and methods of that instance in an event listener. The following example accesses the id of the Button control that triggered the event:
<?xml version="1.0"?>
<!-- events/AccessingCurrentTarget.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myEventHandler(e:Event):void {
Alert.show("The button '" + e.currentTarget.id + "' was clicked.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me" click="myEventHandler(event)"/>
</s:Application>
You can access members of the currentTarget. If you do not cast the current target to a specific type, the compiler assumes that it is of type Object. Objects can have any property or method because the Object type is dynamic in ActionScript. Therefore, when accessing methods and properties of the currentTarget, it is best practice to cast currentTarget to whatever class you anticipate will dispatch that event. This gives you strong type checking at compile time, and helps avoid the risk of throwing a run-time error.
The following example casts the current target to a TextInput class before calling the selectRange() method, but does not cast it before trying to set the tmesis property. The tmesis property does not exist on the TextInput class. This illustrates that you will get a run-time error but not a compile-time error when you try to access members that don't exist, unless you cast currentTarget to a specific type so that type checking can occur:
<?xml version="1.0"?>
<!-- events/InvokingOnCurrentTarget.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"
width="500">
<fx:Script>
<![CDATA[
import mx.core.UIComponent;
private function tiHandler(e:Event):void {
/*
The following enforces type checking:
*/
TextInput(e.currentTarget).selectRange(0,3);
/*
The following throws a run-time error but not a compile-time error:
e.currentTarget.tmesis = 4;
*/
/*
... unless you cast it to the expected type like the following. Then
the compiler throws an error.
TextInput(e.currentTarget).tmesis = 4;
*/
}
]]>
</fx:Script>
<s:TextInput id="ti1" click="tiHandler(event)"
text="When you click on this control, the first three characters are selected."
width="400"/>
</s:Application>
You could also cast currentTarget to UIComponent or some other more general class that still has methods of display objects. That way, if you don't know exactly which control will dispatch an event, at least you can ensure there is some type checking.
You can also access methods and properties of the target property, which contains a reference to the current node in the display list. For more information, see About the target and currentTarget properties.
There are several strategies that you can employ when you register event handlers with your Flex controls:
Define an event handler inline. This binds a call to the handler function to the control that triggers the event.
<?xml version="1.0"?>
<!-- events/SimplerEventHandler.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myEventHandler(event:Event):void {
Alert.show("An event occurred.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me" click="myEventHandler(event)"/>
</s:Application>
In this example, whenever the user clicks the Button control, Flex calls the myClickHandler() function.
For more information on defining event handlers inline, see Defining event listeners inline.
Use the addEventListener() method, as follows:
<?xml version="1.0"?>
<!-- events/SimpleEventHandler.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="initApp();">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function initApp():void {
b1.addEventListener(MouseEvent.CLICK, myEventHandler);
}
private function myEventHandler(event:Event):void {
Alert.show("An event occurred.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
</s:Application>
As with the previous example, whenever the user clicks the Button control, Flex calls the myClickHandler() handler function. However, registering your event handlers using this method provides more flexibility. You can register multiple components with this event handler, add multiple handlers to a single component, or remove the handler. For more information, see Using the addEventListener() method.
Create an event handler class and register components to use the class for event handling. This approach to event handling promotes code reuse and lets you centralize event handling outside your MXML files. For more information on creating custom event handler classes, see Creating event handler classes.
The simplest method of defining event handlers in applications is to point to a handler function in the component's MXML tag. To do this, you add any of the component's events as a tag attribute followed by an ActionScript statement or function call.
You add an event handler inline using the following syntax:
<s:tag_name event_name="handler_function"/>
For example, to listen for a Button control's click event, you add a statement in the <mx:Button> tag's click attribute. If you add a function, you define that function in an ActionScript block. The following example defines the submitForm() function as the handler for the Button control's click event:
<fx:Script><![CDATA[
function submitForm():void {
// Do something.
}
]]></fx:Script>
<s:Button label="Submit" click="submitForm();"/>
Event handlers can include any valid ActionScript code, including code that calls global functions or sets a component property to the return value. The following example calls the trace() global function:
<s:Button label="Get Ver" click="trace('The button was clicked');"/>
There is one special parameter that you can pass in an inline event handler definition: the event parameter. If you add the event keyword as a parameter, Flex passes the Event object and inside the handler function, you can then access all the properties of the Event object.
The following example passes the Event object to the submitForm() handler function and specifies it as type MouseEvent:
<?xml version="1.0"?>
<!-- events/MouseEventHandler.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myEventHandler(event:MouseEvent):void {
// Do something with the MouseEvent object.
Alert.show("An event of type '" + event.type + "' occurred.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me" click="myEventHandler(event)"/>
</s:Application>
It is best practice to include the event keyword when you define all inline event listeners and to specify the most stringent Event object type in the resulting listener function (for example, specify MouseEvent instead of Event).
You can use the Event object to access a reference to the target object (the object that dispatched the event), the type of event (for example, click), or other relevant properties, such as the row number and value in a list-based control. You can also use the Event object to access methods and properties of the target component, or the component that dispatched the event.
Although you will most often pass the entire Event object to an event listener, you can just pass individual properties, as the following example shows:
<?xml version="1.0"?>
<!-- events/PropertyHandler.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myEventHandler(s:String):void {
Alert.show("Current Target: " + s);
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me" click="myEventHandler(event.currentTarget.id)"/>
</s:Application>
Registering an event listener inline provides less flexibility than using the addEventListener() method to register event listeners. The drawbacks are that you cannot set the useCapture or priority properties on the Event object and that you cannot remove the listener once you add it.
The addEventListener() method lets you register event listener functions with the specified control or object. The following example adds the myClickListener() function to the b1 instance of a Button control. When the user clicks b1, Flex calls the myClickListener() method:
b1.addEventListener(MouseEvent.CLICK, myClickListener);
The addEventListener() method has the following signature:
componentInstance.addEventListener( event_type:String, event_listener:Function, use_capture:Boolean, priority:int, weakRef:Boolean )
The event_type argument is the kind of event that this component dispatches. This can be either the event type String (for example, "click" or "mouseOut") or the event type static constant (such as MouseEvent.CLICK or MouseEvent.MOUSE_OUT). This argument is required.
The constants provide an easy way to refer to specific event types. You should use these constants instead of the strings that they represent. If you misspell a constant name in your code, the compiler catches the mistake. If you instead use strings and make a typographical error, it can be harder to debug and could lead to unexpected behavior.
You should use the constants wherever possible. For example, when you are testing to see whether an Event object is of a certain type, use the following code:
if (myEventObject.type == MouseEvent.CLICK) {/* your code here */}
Do not use the following code:
if (myEventObject.type == "click") {/* your code here */}
The event_listener argument is the function that handles the event. This argument is required.
The use_capture parameter of the addEventListener() method lets you control the phase in the event flow in which your listener will be active. It sets the value of the useCapture property of the Event object. If useCapture is set to true, your listener is active during the capturing phase of the event flow. If useCapture is set to false, your listener is active during the targeting and bubbling phases of the event flow, but not during the capturing phase. The default value is determined by the type of event, but is false in most cases.
To listen for an event during all phases of the event flow, you must call addEventListener() twice, once with the useCapture parameter set to true, and again with use_capture set to false. This argument is optional. For more information, see Capturing phase.
The priority parameter sets the priority for that event listener. The higher the number, the sooner that event handler executes relative to other event listeners for the same event. Event listeners with the same priority are executed in the order that they were added. This parameter sets the priority property of the Event object. The default value is 0, but you can set it to negative or positive integer values. If several event listeners are added without priorities, the earlier a listener is added, the sooner it is executed. For more information on setting priorities, see Event priorities.
The weakRef parameter provides you with some control over memory resources for listeners. A strong reference (when weakRef is false) prevents the listener from being garbage collected. A weak reference (when weakRef is true) does not. The default value is false.
When you add a listener function and that function is invoked, Flex implicitly creates an Event object for you and passes it to the listener function. You must declare the Event object in the signature of your listener function.
If you add an event listener by using the addEventListener() method, you are required to declare an event object as a parameter of the listener_function , as the following example shows:
b1.addEventListener(MouseEvent.CLICK, performAction);
In the listener function, you declare the Event object as a parameter, as follows:
public function performAction(e:MouseEvent):void {
...
}
The following example defines a new handler function myClickListener(). It then registers the click event of the Button control with that handler. When the user clicks the button, Flex calls the myClickHandler() function.
<?xml version="1.0"?>
<!-- events/AddEventListenerExample.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"
initialize="createListener()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function createListener():void {
b1.addEventListener(MouseEvent.CLICK, myClickHandler, false, 0);
}
private function myClickHandler(e:MouseEvent):void {
Alert.show("The button was clicked.");
}
]]>
</fx:Script>
<s:Button label="Click Me" id="b1"/>
</s:Application>
You can add event listeners with the addEventListener() method inline with the component definition. The following Button control definition adds the call to the addEventListener() method inline with the Button control's initialize property:
<?xml version="1.0"?>
<!-- events/CallingAddEventListenerInline.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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function myClickHandler(event:Event):void {
Alert.show("The button was clicked.");
}
]]>
</fx:Script>
<s:Button id='b1'
label="Click Me"
initialize='b1.addEventListener(MouseEvent.CLICK, myClickHandler, false, 1);'
/>
</s:Application>
This is the equivalent of defining the event handler inline. However, defining a handler by using the addEventListener() method rather than setting click=" handler_function " lets you set the value of the useCapture and priority properties of the Event object. Furthermore, you cannot remove a handler added inline, but when you use the addEventListener() method to add a handler, you can call the removeEventListener() method to remove that handler.
Rather than passing the name of an event listener function to the addEventListener() method, you can define an inner function (also known as a closure).
In the following example, the nested inner function is called when the button is clicked:
<?xml version="1.0"?>
<!-- events/AddingInnerFunctionListener.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="initApp()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function initApp():void {
b1.addEventListener("click",
function(e:Event):void {
Alert.show("The button was clicked.");
}
);
}
]]>
</fx:Script>
<s:Button id='b1' label="Click Me"/>
</s:Application>
Function closures are created any time a function is executed apart from an object or a class. They retain the scope in which they were defined. This creates interesting results when a function is passed as an argument or a return value into a different scope.
For example, the following code creates two functions: foo(), which returns a nested function named rectArea() that calculates the area of a rectangle, and bar(), which calls foo() and stores the returned function closure in a variable named myProduct. Even though the bar() function defines its own local variable x (with a value of 2), when the function closure myProduct() is called, it retains the variable x (with a value of 40) defined in function foo(). The bar() function therefore returns the product of the numbers in the TextInput controls, rather than 8.
<?xml version="1.0"?>
<!-- events/FunctionReturnsFunction.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="foo()">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
[Bindable]
private var answer:String;
private function foo():Function {
var x:int = int(ti1.text);
function rectArea(y:int):int { // function closure defined
return x * y;
}
return rectArea;
}
private function bar():void {
var x:int = 2; // ignored
var y:int = 4; // ignored
var myProduct:Function = foo();
answer = myProduct(int(ti2.text)); // function closure called
}
]]>
</fx:Script>
<s:Form width="107">
<s:FormItem label="X">
<s:TextInput id="ti1" text="10" width="37" textAlign="right"/>
</s:FormItem>
<s:FormItem label="Y" width="71">
<s:TextInput id="ti2" text="20" width="38" textAlign="right"/>
</s:FormItem>
<s:Label id="label1" text="{answer}" width="71" textAlign="right"/>
</s:Form>
<s:Button id='b1' label="Compute Product" click="bar()"/>
</s:Application>
If the listener that you pass to addEventListener() method is a nested inner function, you should not pass true for the useWeakReference argument. For example:
addEventListener("anyEvent",
function(e:Event) { /* My listener function. */ },
false, 0, true);
In this example, passing true as the last argument can lead to unexpected results. To Flex, an inner function is actually an object, and can be freed by the garbage collector. If you set the value of the useWeakReference argument to true, as shown in the previous example, there are no persistent references at all to the inner function. The next time the garbage collector runs, it might free the function, and the function will not be called when the event is triggered.
If there are other references to the inner function (for example, if you saved it in another variable), the garbage collector will not free it.
Regular class-level member functions are not subject to garbage collection; as a result, you can set the value of the useWeakReference argument to true and they will not be garbage collected.
It is a good idea to remove any handlers that will no longer be used. This removes references to objects so that they can be targeted for garbage collection. You can use the removeEventListener() method to remove an event handler that you no longer need. All components that can call addEventListener() can also call the removeEventListener() method. The syntax for the removeEventListener() method is as follows:
componentInstance.removeEventListener(event_type:String, listener_function:Function, use_capture:Boolean)
For example, consider the following code:
myButton.removeEventListener(MouseEvent.CLICK, myClickHandler);
The event_type and listener_function parameters are required. These are the same as the required parameters for the addEventListener() method.
The use_capture parameter is also identical to the parameter used in the addEventListener() method. Recall that you can listen for events during all event phases by calling addEventListener() twice: once with use_capture set to true, and again with it set to false. To remove both event listeners, you must call removeEventListener() twice: once with use_capture set to true, and again with it set to false.
You can remove only event listeners that you added with the addEventListener() method in an ActionScript block. You cannot remove an event listener that was defined in the MXML tag, even if it was registered using a call to the addEventListener() method that was made inside a tag attribute.
The following sample application shows what type of handler can be removed and what type cannot:
<?xml version="1.0"?>
<!-- events/RemoveEventListenerExample.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"
initialize="createHandler(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function createHandler(e:Event):void {
b1.addEventListener(MouseEvent.CLICK, myClickHandler);
}
private function removeMyHandlers(e:Event):void {
/* Remove listener for b1's click event because it was added
with the addEventListener() method. */
b1.removeEventListener(MouseEvent.CLICK, myClickHandler);
/* Does NOT remove the listener for b2's click event because it
was added inline in an MXML tag. */
b2.removeEventListener(MouseEvent.CLICK, myClickHandler);
}
private function myClickHandler(e:Event):void {
Alert.show("The button was clicked.");
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
<s:Button label="Click Me Too" id="b2" click="myClickHandler(event)"/>
<s:Button label="Remove Event Listeners" id="b3" click="removeMyHandlers(event)"/>
</s:Application>
You can create an external class file and use the methods of this class as event handlers. Objects themselves cannot be event handlers, but methods of an object can be. By defining one class that handles all your event handlers, you can use the same event handling logic across applications, which can make your MXML applications more readable and maintainable.
To create a class that handles events, you usually import the flash.events.Event class. You also usually write an empty constructor. The following ActionScript class file calls the Alert control's show() method whenever it handles an event with the handleAllEvents() method:
// events/MyEventHandler.as
package { // Empty package.
import flash.events.Event;
import mx.controls.Alert;
public class MyEventHandler {
public function MyEventHandler() {
// Empty constructor.
}
public function handleAllEvents(event:Event):void {
Alert.show("Some event happened.");
}
}
}
In your MXML file, you declare a new instance of MyEventHandler and use the addEventListener() method to register its handleAllEvents() method as a handler to the Button control's click event, as the following example shows:
<?xml version="1.0"?>
<!-- events/CustomHandler.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"
initialize="createHandler()">
<fx:Script>
<![CDATA[
private var myListener:MyEventHandler = new MyEventHandler();
private function createHandler():void {
b1.addEventListener(MouseEvent.CLICK, myListener.handleAllEvents);
}
]]>
</fx:Script>
<s:Button label="Submit" id="b1"/>
</s:Application>
The best approach is to define the event handler's method as static. When you make the event handler method static, you are not required to instantiate the class inside your MXML application. The following createHandler() function registers the handleAllEvents() method as an event handler without instantiating the MyStaticEventHandler class:
<?xml version="1.0"?>
<!-- events/CustomHandlerStatic.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"
initialize="createHandler()">
<fx:Script>
<![CDATA[
private function createHandler():void {
b1.addEventListener(MouseEvent.CLICK, MyStaticEventHandler.handleAllEvents);
}
]]>
</fx:Script>
<s:Button label="Submit" id="b1"/>
</s:Application>
In the class file, you just add the static keyword to the method signature:
// events/MyStaticEventHandler.as
package { // Empty package.
import flash.events.Event;
import mx.controls.Alert;
public class MyStaticEventHandler {
public function MyStaticEventHandler() {
// Empty constructor.
}
public static function handleAllEvents(event:Event):void {
Alert.show("Some event happened.");
}
}
}
Store your event listener class in a directory in your source path. You can also store your ActionScript class in the same directory as your MXML file, although it is not recommended.
You can define multiple event handler functions for a single event in two ways. When defining events inside MXML tags, you separate each new handler function with a semicolon. The following example adds the submitForm() and debugMessage() functions as handlers of the click event:
<?xml version="1.0"?>
<!-- events/MultipleEventHandlersInline.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[
[Bindable]
private var s:String = "";
private function submitForm(e:Event):void {
// Handle event here.
s += "The submitForm() method was called. ";
}
private function debugMessage(e:Event):void {
// Handle event here.
s += "The debugMessage() method was called. ";
}
]]></fx:Script>
<s:Button id="b1"
label="Do Both Actions"
click='submitForm(event); debugMessage(event);'
/>
<s:Label id="l1" text="{s}"/>
<s:Button id="b2" label="Reset" click="s='';"/>
</s:Application>
For events added with the addEventListener() method, you can add any number of handlers with additional calls to the addEventListener() method. Each call adds a handler function that you want to register to the specified object. The following example registers the submitForm() and debugMessage() handler functions with b1's click event:
<?xml version="1.0"?>
<!-- events/MultipleEventHandlersAS.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="createHandlers(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script><![CDATA[
[Bindable]
private var s:String = "";
public function createHandlers(e:Event):void {
b1.addEventListener(MouseEvent.CLICK, submitForm);
b1.addEventListener(MouseEvent.CLICK, debugMessage);
}
private function submitForm(e:Event):void {
// Handle event here.
s += "The submitForm() method was called. ";
}
private function debugMessage(e:Event):void {
// Handle event here.
s += "The debugMessage() method was called. ";
}
]]></fx:Script>
<s:Button id="b1" label="Do Both Actions"/>
<s:Label id="l1" text="{s}"/>
<s:Button id="b2" label="Reset" click="s='';"/>
</s:Application>
You can mix the methods of adding event handlers to any component; alternatively, you can add handlers inline and with the addEventListener() method. The following example adds a click event handler inline for the Button control, which calls the performAction() method. It then conditionally adds a second click handler to call the logAction() method, depending on the state of the CheckBox control.
<?xml version="1.0"?>
<!-- events/ConditionalHandlers.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"
initialize="initApp(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function initApp(e:Event):void {
cb1.addEventListener(MouseEvent.CLICK, handleCheckBoxChange);
b1.addEventListener(MouseEvent.CLICK, logAction);
}
private function handleCheckBoxChange(e:Event):void {
if (cb1.selected) {
b1.addEventListener(MouseEvent.CLICK, logAction);
ta1.text += "Added log listener." + "\n";
} else {
b1.removeEventListener(MouseEvent.CLICK, logAction);
ta1.text += "Removed log listener." + "\n";
}
}
private function performAction(e:Event):void {
Alert.show("You performed the action.");
}
private function logAction(e:Event):void {
ta1.text += "Action performed: " + e.type + ".\n";
}
]]>
</fx:Script>
<s:Button label="Perform Action" id="b1" click="performAction(event)"/>
<s:CheckBox id="cb1" label="Log?" selected="true"/>
<s:TextArea id="ta1" height="200" width="300"/>
</s:Application>
You can set the order in which event listeners are called by using the priority parameter of the addEventListener() method. You cannot set a priority for a listener function if you added the event listener using MXML inline. For more information on setting priorities, see Event priorities.
You can register the same listener function with any number of events of the same component, or events of different components. The following example registers a single listener function, submitForm(), with two different buttons:
<?xml version="1.0"?>
<!-- events/OneHandlerTwoComponentsInline.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[
import mx.controls.Alert;
private function submitForm(e:Event):void {
// Handle event here.
Alert.show("Current Target: " + e.currentTarget.id);
}
]]>
</fx:Script>
<s:Button id="b1"
label="Click Me"
click="submitForm(event)"/>
<s:Button id="b2"
label="Click Me, Too"
click="submitForm(event)"/>
</s:Application>
When you use the addEventListener() method to register a single listener to handle the events of multiple components, you must use a separate call to the addEventListener() method for each instance, as the following example shows:
<?xml version="1.0"?>
<!-- events/OneHandlerTwoComponentsAS.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="createHandlers(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
public function createHandlers(e:Event):void {
b1.addEventListener(MouseEvent.CLICK, submitForm);
b2.addEventListener(MouseEvent.CLICK, submitForm);
}
private function submitForm(e:Event):void {
// Handle event here.
Alert.show("Current Target: " + e.currentTarget.id);
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
<s:Button id="b2" label="Click Me, Too"/>
</s:Application>
When doing this, you should add logic to the event listener that processes the type of event. The event target (or object that dispatched the event) is added to the Event object for you. No matter what triggered the event, you can conditionalize the event processing based on the target or type properties of the Event object. Flex adds these two properties to all Event objects.
The following example registers a single listener function (myEventHandler()) to the click event of a Button control and the click event of a CheckBox control. To detect what type of object called the event listener, the listener checks the className property of the target in the Event object in a case statement.
<?xml version="1.0"?>
<!-- events/ConditionalTargetHandler.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="initApp()">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
public function initApp():void {
button1.addEventListener(MouseEvent.CLICK, myEventHandler);
cb1.addEventListener(MouseEvent.CLICK, myEventHandler);
}
public function myEventHandler(event:Event):void {
switch (event.currentTarget.className) {
case "Button":
// Process Button click.
Alert.show("You clicked the Button control.");
break;
case "CheckBox":
// Process CheckBox click.
Alert.show("You clicked the CheckBox control.");
break;
}
}
]]>
</fx:Script>
<s:Button label="Click Me" id="button1"/>
<s:CheckBox label="Select Me" id="cb1"/>
</s:Application>
You can pass additional parameters to listener functions depending on how you add the listeners. If you add a listener with the addEventListener() method, you cannot pass any additional parameters to the listener function by arbitrarily adding new parameters to the function signature. The default listener function can declare only a single argument, the Event object (or one of its subclasses).
For example, the following code throws an error because the clickListener() method expects two arguments:
<fx:Script>
public function addListeners():void {
b1.addEventListener(MouseEvent.CLICK,clickListener);
}
public function clickListener(e:MouseEvent, a:String):void { ... }
</fx:Script>
<mx:Button id="b1"/>
Because the second parameter of the addEventListener() method is a function, you can define that function and pass the event object plus any additional parameters through to a different handler. The following example creates a new function in the addEventListener() method, add two parameters to the new handler's call, and then handles all of the parameters in the myClickListener() method.
<?xml version="1.0" encoding="utf-8"?>
<!-- events/CustomListenerFunction.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="initApp(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
private var specialParam1:String;
private var specialParam2:String = "42";
private function initApp(e:Event):void {
assignSpecialParam(e);
/* Change the value of specialParam whenever the user changes it
in the TextInput and clicks the button. */
ti1.addEventListener("focusOut", assignSpecialParam);
/* Define the pass-through method in the addEventListener() method call.
You can add any number of parameters, as long as teh target method's
signature agrees. */
myButton.addEventListener(MouseEvent.CLICK, function (e:MouseEvent):void {
myClickListener(e, specialParam1, specialParam2);
}
);
}
private function assignSpecialParam(e:Event):void {
specialParam1 = ti1.text;
}
/* This method acts as the event listener, and it has any
number of parameters that we defined in the addEventListener() call. */
private function myClickListener(e:MouseEvent, s1:String, s2:String) : void {
myButton.label = s1 + " " + s2;
}
]]>
</fx:Script>
<s:Button id="myButton" label="Click Me"/>
<s:TextInput id="ti1" text="Enter a custom String here." width="250"/>
</s:Application>
Another approach to passing additional parameters to the listener function is to define them in the listener function and then call the final method with those parameters. If you define an event listener inline (inside the MXML tag), you can add any number of parameters as long as the listener function's signature agrees with that number of parameters.
The following example passes a string and the Event object to the runMove() method:
<?xml version="1.0"?>
<!-- events/MultipleHandlerParametersInline.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 runMove(dir:String, e:Event):void {
if (dir == "up") {
moveableButton.y = moveableButton.y - 5;
} else if (dir == "down") {
moveableButton.y = moveableButton.y + 5;
} else if (dir == "left") {
moveableButton.x = moveableButton.x - 5;
} else if (dir == "right") {
moveableButton.x = moveableButton.x + 5;
}
}
]]>
</fx:Script>
<mx:Canvas height="100%" width="100%">
<s:Button id="moveableButton"
label="{moveableButton.x.toString()},{moveableButton.y.toString()}"
x="75"
y="100"
width="80"
/>
</mx:Canvas>
<s:VGroup horizontalAlign="center">
<s:Button id="b1"
label="Up"
click='runMove("up",event);'
width="75"/>
<s:HGroup horizontalAlign="center">
<mx:Button id="b2"
label="Left"
click='runMove("left",event);'
width="75"/>
<s:Button id="b3"
label="Right"
click='runMove("right",event);'
width="75"/>
</s:HGroup>
<s:Button id="b4"
label="Down"
click='runMove("down",event);'
width="75"/>
</s:VGroup>
</s:Application>
You can manually dispatch events using a component instance's dispatchEvent() method. All components that extend UIComponent have this method. The method is inherited from the EventDispatcher class, which UIComponent extends.
The syntax for the dispatchEvent() method is as follows:
objectInstance.dispatchEvent(event:Event):Boolean
When dispatching an event, you must create a new Event object. The syntax for the Event object constructor is as follows:
Event(event_type:String, bubbles:Boolean, cancelable:Boolean)
The event_type parameter is the type property of the Event object. The bubbles and cancelable parameters are optional and both default to false. For information on bubbling and capturing, see Event propagation.
You can use the dispatchEvent() method to dispatch any event you want, not just a custom event. You can dispatch a Button control's click event, even though the user did not click a Button control, as in the following example:
<?xml version="1.0"?>
<!-- events/DispatchEventExample.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"
initialize="createListener(event);">
<fx:Script><![CDATA[
import mx.controls.Alert;
private function createListener(e:Event):void {
b1.addEventListener(MouseEvent.MOUSE_OVER, myEventHandler);
b1.addEventListener(MouseEvent.CLICK, myClickHandler);
}
private function myEventHandler(e:Event):void {
var result:Boolean = b1.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));
}
private function myClickHandler(e:Event):void {
Alert.show("The event dispatched by the MOUSE_OVER was of type '" + e.type + "'.");
}
]]></fx:Script>
<s:Button id="b1" label="Click Me"/>
</s:Application>
You can also manually dispatch an event in an MXML tag. In the following example, moving the mouse pointer over the button triggers the button's click event:
<?xml version="1.0"?>
<!-- events/DispatchEventExampleInline.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"
initialize="createListener(event);">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function createListener(e:Event):void {
b1.addEventListener(MouseEvent.CLICK, myClickHandler);
}
private function myClickHandler(e:Event):void {
Alert.show("The event dispatched by the MOUSE_OVER was of type '" + e.type + "'.");
}
]]>
</fx:Script>
<s:Button id="b1"
label="Click Me"
mouseOver="b1.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));"
/>
</s:Application>
Your application is not required to handle the newly dispatched event. If you trigger an event that has no listeners, Flex ignores the event.
You can set properties of the Event object in ActionScript, but you cannot add new properties because the object is not dynamic. The following example intercepts a click event. It then creates a new MouseEvent object and dispatches it as a doubleClick event. In addition, it sets the value of the shiftKey property of the MouseEvent object to true, to simulate a Shift-click on the keyboard.
<?xml version="1.0"?>
<!-- events/DispatchCustomizedEvent.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="addListeners()">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script><![CDATA[
private function customLogEvent(e:MouseEvent):void {
l1.text = String(e.currentTarget.id);
l2.text = String(e.type);
l3.text = String(e.shiftKey);
// Remove current listener to avoid recursion.
e.currentTarget.removeEventListener("doubleClick", customLogEvent);
}
private function handleEvent(e:MouseEvent):void {
// Add new handler for custom event about to be dispatched.
e.currentTarget.addEventListener("doubleClick", customLogEvent);
// Create new event object.
var mev:MouseEvent = new MouseEvent("doubleClick");
// Customize event object.
mev.shiftKey = true;
// Dispatch custom event.
e.currentTarget.dispatchEvent(mev);
}
private function addListeners():void {
b1.addEventListener("click",handleEvent);
b2.addEventListener("click",handleEvent);
}
]]></fx:Script>
<s:Button id="b1" label="Click Me (b1)"/>
<s:Button id="b2" label="Click Me (b2)"/>
<s:Form>
<s:FormItem label="Current Target:">
<s:Label id="l1"/>
</s:FormItem>
<s:FormItem label="Event Type:">
<s:Label id="l2"/>
</s:FormItem>
<s:FormItem label="Shift Key Pressed:">
<s:Label id="l3"/>
</s:FormItem>
</s:Form>
</s:Application>
If you want to add custom properties to an Event object, you must extend the Event object and define the new properties in your own custom class. You can then manually dispatch your custom events with the dispatchEvent() method, as you would any event.
If you create a custom ActionScript class that dispatches its own events but does not extend UIComponent, you can extend the flash.events.EventDispatcher class to get access to the addEventListener(), removeEventListener(), and dispatchEvent() methods.
To make your code more efficient, you can check to see if the intended target of a dispatched event is listening for the event. If it is not, then there is no reason to dispatch the event. This is especially true if the event bubbles because that means many calls will be made by the system while it searches for a target. You can check by using the hasEventListner() and willTrigger() methods.
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.
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:
<mx:Button label="OK" click="trace(event.currentTarget.label)"/>
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.
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:
myPanel.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler, true);
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.
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.
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 Apache Flex.
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.
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:
<?xml version="1.0"?>
<!-- events/DisplayCurrentTargetInfo.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">
<fx:Script><![CDATA[
import mx.controls.Alert;
private function showInfo(e:MouseEvent):void {
Alert.show("Phase: " + e.eventPhase + "\n" +
"ID: " + e.currentTarget.id + "\n" +
"Label: " + e.currentTarget.label + "\n" +
"Font Size: " + e.currentTarget.getStyle("fontSize"), "Current Target Info");
}
]]></fx:Script>
<s:Button id="b1" label="Click Me" click="showInfo(event)"/>
</s:Application>
During any phase, you can stop the traversal of the display list by calling one of the following methods on the Event object:
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:
<?xml version="1.0"?>
<!-- events/StoppingPropagation.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"
initialize="init(event);">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import flash.events.MouseEvent;
import flash.events.Event;
public function init(e:Event):void {
p1.addEventListener(MouseEvent.MOUSE_DOWN, showAlert);
tw1.addEventListener(MouseEvent.MOUSE_DOWN, showAlert);
tw1.addEventListener(Event.CLOSE, closeWindow);
p2.addEventListener(MouseEvent.MOUSE_DOWN, showAlertWithoutStoppingPropagation);
tw2.addEventListener(MouseEvent.MOUSE_DOWN, showAlertWithoutStoppingPropagation);
tw2.addEventListener(Event.CLOSE, closeWindow);
}
public function showAlert(e:Event):void {
Alert.show("Alert!\n" + "Current Target: " + e.currentTarget + "\n" +
"Phase: " + e.eventPhase);
e.stopImmediatePropagation();
}
public function showAlertWithoutStoppingPropagation(e:Event):void {
Alert.show("Alert!\n" + "Current Target: " + e.currentTarget + "\n" +
"Phase: " + e.eventPhase);
}
public function closeWindow(e:Event):void {
p1.removeChild(tw1);
}
]]>
</fx:Script>
<s:Panel id="p1" title="Stops Propagation">
<mx:TitleWindow id="tw1"
width="300"
height="100"
showCloseButton="true"
title="Title Window 1">
<s:Button label="Click Me"/>
<s:TextArea id="ta1"/>
</mx:TitleWindow>
</s:Panel>
<s:Panel id="p2" title="Does Not Stop Propagation">
<mx:TitleWindow id="tw2"
width="300"
height="100"
showCloseButton="true"
title="Title Window 2">
<s:Button label="Click Me"/>
<s:TextArea id="ta2"/>
</mx:TitleWindow>
</s:Panel>
</s:Application>
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>
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>
You can register any number of event listeners with a single event. Flex registers event listeners in the order in which the addEventListener() methods are called. Flex then typically calls the listener functions when the event occurs in the order in which they were registered. However, if you register some event listeners inline and some with the addEventListener() method, the order in which the listeners are called for a single event can be unpredictable.
You can change the order in which Flex calls event listeners by using the priority parameter of the addEventListener() method. It is the fourth argument of the addEventListener() method.
Flex calls event listeners in priority order, from highest to lowest. The highest priority event is called first. In the following example, Flex calls the verifyInputData() method before the saveInputData() function. The verifyInputData() method has the highest priority. The last method to be called is returnResult() because the value of its priority parameter is lowest.
<?xml version="1.0"?>
<!-- events/ShowEventPriorities.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="initApp()">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
private function returnResult(e:Event):void {
ta1.text += "returnResult() method called last (priority 1)\n";
}
private function verifyInputData(e:Event):void {
ta1.text += "verifyInputData() method called first (priority 3)\n";
}
private function saveInputData(e:Event):void {
ta1.text += "saveInputData() method called second (priority 2)\n";
}
private function initApp():void {
b1.addEventListener(MouseEvent.CLICK, returnResult, false, 1);
b1.addEventListener(MouseEvent.CLICK, saveInputData, false, 2);
b1.addEventListener(MouseEvent.CLICK, verifyInputData, false, 3);
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
<s:TextArea id="ta1" height="200" width="300"/>
</s:Application>
You can set the event priority to any valid integer, positive or negative. The default value is 0. If multiple listeners have the same priority, Flex typically calls them in the order in which they were registered, although the order is not guaranteed.
If you want to change the priority of an event listener once the event listener has already been defined, you must remove the listener by calling the removeEventListener() method. You add the event again with the new priority.
The priority parameter of the addEventListener() method is not an official part of the DOM Level 3 events model. ActionScript 3.0 provides it so that programmers can be more flexible when organizing their event listeners.
If your listeners rely on a specific order of execution, you can call one listener function from within another, or dispatch a new event from within the first event listener. For more information on manually dispatching events, see Manually dispatching events.
Depending on the event type, the Event object can have a wide range of properties. These properties are based on those defined in the W3C specification http://www.w3.org/TR/DOM-Level-3-Events/), but Flex does not implement all of these.
When you declare an Event object in a listener function, you can declare it of type Event, or you can specify a subclass of the Event object. In the following example, you specify the event object as type MouseEvent:
public function performAction(e:MouseEvent):void {
...
}
Most controls generate an object that is of a specific event type; for example, a mouse click generates an object of type MouseEvent. By specifying a more specific event type, you can access specific properties without having to cast the Event object to something else. In addition, some subclasses of the Event object have methods that are unique to them. For example, the LogEvent has a getLevelString() method, which returns the log level as a String. The generic Event object does not have this method.
An event object that you define at run time can be a subclass of the compile-time type. You can access the event-specific properties inside an event listener even if you did not declare the specific event type, as long as you cast the Event object to a specific type. In the following example, the function defines the object type as Event. However, inside the function, in order to access the localX and localY properties, which are specific to the MouseEvent class, you must cast the Event object to be of type MouseEvent.
<?xml version="1.0"?>
<!-- events/AccessEventSpecificProperties.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"
initialize="addListeners()">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function customLogEvent(e:Event):void {
var a:MouseEvent = MouseEvent(e);
Alert.show("Y: " + a.localY + "\n" + "X: " + a.localX);
}
private function addListeners():void {
b1.addEventListener(MouseEvent.CLICK, customLogEvent);
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
</s:Application>
If you declare the Event object as a specific type, you are not required to cast that object in the handler, as the following example shows:
private function customLogEvent(e:MouseEvent):void { ... }
In the previous example, you can also cast the Event object for only the property access, using the syntax shown in the following example:
<?xml version="1.0"?>
<!-- events/SinglePropertyAccess.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"
initialize="addListeners()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function customLogEvent(e:Event):void {
Alert.show("Y: " + MouseEvent(e).localY + "\n" + "X: " + MouseEvent(e).localX);
}
private function addListeners():void {
b1.addEventListener(MouseEvent.CLICK, customLogEvent);
}
]]>
</fx:Script>
<s:Button id="b1" label="Click Me"/>
</s:Application>
This approach can use less memory and system resources, but it is best to declare the event's type as specifically as possible.
Each of the Event object's subclasses provides additional properties and event types that are unique to that category of events. The MouseEvent class defines several event types related to that input device, including the CLICK, DOUBLE_CLICK, MOUSE_DOWN, and MOUSE_UP event types.
For a list of types for each Event subclass, see the subclass's entry in ActionScript 3.0 Reference for the Adobe Flash Platform .
It is common for applications to respond to a key or series of keys and perform some action—for example, Control+q to quit the application. While Flash Player supports all the basic functionality of key combinations from the underlying operating system, it also lets you override or trap any key or combination of keys to perform a custom action.
In some cases, you want to trap keys globally, meaning no matter where the user is in the application, their keystrokes are recognized by the application and the action is performed. Flex recognizes global keyboard events whether the user is hovering over a button or the focus is inside a TextInput control.
A common way to handle global key presses is to create a listener for the KeyboardEvent.KEY_DOWN or KeyboardEvent.KEY_UP event on the application. Listeners on the application container are triggered every time a key is pressed, regardless of where the focus is (as long as the focus is in the application on not in the browser controls or outside of the browser). Inside the handler, you can examine the key code or the character code using the charCode and keyCode properties of the KeyboardEvent class, as the following example shows:
<?xml version="1.0"?>
<!-- events/TrapAllKeys.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="initApp();">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
private function initApp():void {
FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
}
private function keyHandler(event:KeyboardEvent):void {
t1.text = event.keyCode + "/" + event.charCode;
}
]]>
</fx:Script>
<s:TextInput id="myTextInput"/>
<s:Label id="t1"/>
</s:Application>
To run this example, you must first set the focus to something inside the application, such as the TextInput control, by clicking on it.
Because any class that extends UIComponent dispatches the keyUp and keyDown events, you can also trap keys pressed when the focus is on an individual component.
You can access the keyCode and charCode properties to determine what key was pressed and trigger other actions as a result. The keyCode property is a numeric value that corresponds to the value of a key on the keyboard. The charCode property is the numeric value of that key in the current character set (the default character set is UTF-8, which supports ASCII). The primary difference between the key code and character values is that a key code value represents a particular key on the keyboard (the 1 on a keypad is different than the 1 in the top row, but the 1 on the keyboard and the key that generates the ! are the same key), and the character value represents a particular character (the R and r characters are different).
The mappings between keys and key codes are device and operating system dependent. ASCII values, on the other hand, are available in the ActionScript documentation.
The following example shows the character and key code values for the keys you press. When you run this example, you must be sure to put the focus in the application before beginning.
<?xml version="1.0"?>
<!-- charts/ShowCharAndKeyCodes.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="init()"
width="650">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script><![CDATA[
import flash.events.KeyboardEvent;
private function init():void {
ti1.setFocus();
this.addEventListener(KeyboardEvent.KEY_DOWN, trapKeys);
}
private function trapKeys(e:KeyboardEvent):void {
ta1.text = String(e.toString());
l1.text = numToChar(e.charCode) + " (" + String(e.charCode) + ")";
l2.text = numToChar(e.keyCode) + " (" + String(e.keyCode) + ")";
}
private function numToChar(num:int):String {
if (num > 47 && num < 58) {
var strNums:String = "0123456789";
return strNums.charAt(num - 48);
} else if (num > 64 && num < 91) {
var strCaps:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return strCaps.charAt(num - 65);
} else if (num > 96 && num < 123) {
var strLow:String = "abcdefghijklmnopqrstuvwxyz";
return strLow.charAt(num - 97);
} else {
return num.toString();
}
}
]]></fx:Script>
<s:TextInput width="50%" id="ti1"/>
<s:Panel id="mainPanel" width="100%" height="100%">
<s:Form>
<s:FormItem label="Char (Code)">
<s:Label id="l1"/>
</s:FormItem>
<s:FormItem label="Key (Code)">
<s:Label id="l2"/>
</s:FormItem>
<s:FormItem label="Key Event">
<s:TextArea id="ta1" width="500" height="200" editable="false"/>
</s:FormItem>
</s:Form>
</s:Panel>
</s:Application>
You can listen for specific keys or combinations of keys by using a conditional operator in the KeyboardEvent handler. The following example listens for the combination of the Shift key plus the q key and prompts the user to close the browser window if they press those keys at the same time:
<?xml version="1.0"?>
<!-- events/TrapQKey.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="initApp();">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
private function initApp():void {
FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_UP,keyHandler);
// Set the focus somewhere inside the application.
ta1.setFocus();
}
//This function quits the application if the user presses Shift+Q.
private function keyHandler(event:KeyboardEvent):void {
var bShiftPressed:Boolean = event.shiftKey;
if (bShiftPressed) {
var curKeyCode:int = event.keyCode;
if (curKeyCode == 81) { // 81 is the keycode value for the Q key
/* Quit the application by closing the browser using JavaScript.
This may not work in all browsers. */
var url:URLRequest = new
URLRequest("javascript:window.close()");
navigateToURL(url,"_self");
}
}
}
]]>
</fx:Script>
<s:TextArea id="ta1" text="Focus here so that Shift+Q will quit the browser."/>
</s:Application>
Notice that this application must have focus when you run it in a browser so that the application can capture keyboard events.
If you define keyUp or keyDown event listeners for both a control and its parent, you will notice that the keyboard event is dispatched for each component because the event bubbles. The only difference is that the currentTarget property of the KeyboardEvent object is changed.
In the following example, the application, the my_vgroup container, and the my_textinput control all dispatch keyUp events to the keyHandler() event listener function:
<?xml version="1.0"?>
<!-- events/KeyboardEventPrecedence.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="initApp();"
width="650">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script><![CDATA[
import mx.core.FlexGlobals;
private function initApp():void {
FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
my_vgroup.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
my_textinput.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
// Set the focus somewhere inside the application.
my_textinput.setFocus();
}
private function keyHandler(event:KeyboardEvent):void {
ta1.text += event.target + "(" + event.currentTarget + "): " +
event.keyCode + "/" + event.charCode + "\n";
}
]]></fx:Script>
<s:VGroup id="my_vgroup">
<s:TextInput id="my_textinput"/>
</s:VGroup>
<s:TextArea id="ta1" height="300" width="550"/>
</s:Application>
When you examine the output, you will notice that the target property of the KeyboardEvent object stays the same because it refers to the original dispatcher of the event (in this case, my_textinput). But the currentTarget property changes depending on what the current node is during the bubbling (in this case, it changes from my_textinput to my_vgroup to the application itself).
The order of calls to the event listener is determined by the object hierarchy and not the order in which the addEventListener() methods were called. Child controls dispatch events before their parents. In this example, for each key pressed, the TextInput control dispatches the event first, the VGroup container next, and finally the application.
When handling a key or key combination that the underlying operating system or browser recognizes, the operating system or browser generally processes the event first. For example, in Microsoft Internet Explorer, pressing Control+w closes the browser window. If you trap that combination in your application, Internet Explorer users never know it, because the browser closes before the ActiveX Flash Player has a chance to react to the event.
The MouseEvent class and all MouseEvent subclasses (such as ChartItemEvent, DragEvent, and LegendMouseEvent) have the following properties that you can use to determine if a specific key was held down when the event occurred:
|
Property |
Description |
|---|---|
|
altKey |
Is set to true if the Alt key was held down when the user pressed the mouse button; otherwise, false. |
|
ctrlKey |
Is set to true if the Control key was held down when the user pressed mouse button; otherwise, false. |
|
shiftKey |
Is set to true if the Shift key was held down when the user pressed mouse button; otherwise, false. |
The following example deletes Button controls, based on whether the user holds down the Shift key while pressing the mouse button:
<?xml version="1.0"?>
<!-- events/DetectingShiftClicks.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="initApp();">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script><![CDATA[
import spark.components.Button;
private function initApp():void {
var b1:Button = new Button();
b1.label = "Button 1";
var b2:Button = new Button();
b2.label = "Button 2";
b1.addEventListener(MouseEvent.CLICK, removeButtons);
b2.addEventListener(MouseEvent.CLICK, removeButtons);
vg1.addElement(b1);
vg1.addElement(b2);
}
private function removeButtons(event:MouseEvent):void {
if (event.shiftKey) {
vg1.removeElement(Button(event.currentTarget));
} else {
event.currentTarget.toolTip = "Shift+click to remove this button.";
}
}
]]></fx:Script>
<s:VGroup id="vg1"/>
<s:Button id="resetButton" label="Reset" click="initApp();"/>
</s:Application>
Navigation
Adobe, Adobe AIR, Adobe Flash Platform and Adobe Flash Player are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries and are used by permission from Adobe. No other license to the Adobe trademarks are granted.