About keyboard events

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.

Handling keyboard events

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>

The executing SWF file for the previous example is shown below:

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.

Understanding the keyCode and charCode properties

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>

The executing SWF file for the previous example is shown below:

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>

The executing SWF file for the previous example is shown below:

Notice that this application must have focus when you run it in a browser so that the application can capture keyboard events.

Understanding KeyboardEvent precedence

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>

The executing SWF file for the previous example is shown below:

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.

Handling keyboard-related mouse events

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>

The executing SWF file for the previous example is shown below: