AS3 Events Explained: Using Event Listeners

by Michael James Williams on February 6, 2009 · 35 comments

in AS3 Concepts Explained

Events are an important concept in AS3, but can be confusing to anyone that hasn’t come across them in other languages before. This post explains what an event and an event listener is, how to use them, and why they are useful for Flash game development.

Put simply, an object can trigger a function when certain conditions are met, by creating a special kind of object called an Event. Let’s look at a concrete example.

An instance of the Timer class will dispatch an event every time it “ticks”:

We can use an event listener to detect these events…

…and trigger a function:

Code for this situation would look like this:

?View Code ACTIONSCRIPT3
exampleTimer:Timer = new Timer( 1000 );	// 1000ms == 1second
exampleTimer.start();
exampleTimer.addEventListener( TimerEvent.TIMER, onTimerTick );

The first two lines simply set up the timer; it already contains the code to fire off an event every time it ticks.

Line 3 adds an event listener to the timer. As you can see, it is told not just the class of event but also the type of event to listen out for. (The classes and types of events for built-in classes and components are all listed in the AS3 LiveDocs.) It’s also told which function to run when this event is detected: in this case, the function is called onTimerTick(). Such a function is called an event handler, and programmers typically name them onSomethingSomething — onMousePress, onPlayersDeath, onFileLoaded.

The event object that the listener detects often contains more information than just what type of event it is. For example, KeyboardEvents store the a number (called a KeyCode) referring to the key that a user presses, and MouseEvents store the position of the mouse when it is clicked. The listener doesn’t just run the handler, it passes the entire event object (and all its internal data) to it as a parameter.

So event handlers always take this form:

?View Code ACTIONSCRIPT3
public function onEventOccured( eventObject:Event )
{
	//do something
}

Nothing, not even the listener calling the handler, pays any attention to any value that the handler might return. Therefore, all handlers have a return type of void.

Our handler for this TimerEvent would look like this:

?View Code ACTIONSCRIPT3
public function onTimerTick( timerEvent:TimerEvent ):void
{
	//do something
}

Note that here I’ve specified that the class of the event being passed through is TimerEvent. Since all events extend the base Event class, you could specify this class as Event without causing any problems, but you wouldn’t be able to retrieve any information specific to that class of event (like keyCode for a KeyboardEvent).

You access the data as you would a property of any other object: using dot notation.

?View Code ACTIONSCRIPT3
public function onKeyPressed( keyboardEvent:KeyboardEvent ):void
{
	trace( "The key pressed was: ", keyboardEvent.keyCode );
}

To remove an event listener from an object (and therefore stop it triggering the handler), simply call removeEventListener with the same parameters that you used to add it:

?View Code ACTIONSCRIPT3
exampleTimer.removeEventListener( TimerEvent.TIMER, onTimerTick );

You can add and remove event listeners from within event handlers, too — even removing the listener that triggered the handler, if you like.

On top of that, it’s possible (and easy) to add multiple listeners to a single event:

?View Code ACTIONSCRIPT3
exampleTimer.addEventListener( TimerEvent.TIMER, onTimerTick );
exampleTimer.addEventListener( TimerEvent.TIMER, onTimerTickWhilePlayerIsAlive );
exampleTimer.addEventListener( TimerEvent.TIMER, onTimerTickWhilePlayerIsHoldingTheSword );

In the above example, you could set up the listeners to trigger different handlers depending on the state of the game, and remove those listeners as the state changed, rather than having a complicated if-then-else block within the handler itself.

Similarly, you can reuse the same handler for different events:

?View Code ACTIONSCRIPT3
menuTimer.addEventListener( TimerEvent.TIMER, onAnyTimerTickOrFinish );
menuTimer.addEventListener( TimerEvent.TIMER_COMPLETE, onAnyTimerTickOrFinish );
gameTimer.addEventListener( TimerEvent.TIMER, onAnyTimerTickOrFinish );
gameTimer.addEventListener( TimerEvent.TIMER_COMPLETE, onAnyTimerTickOrFinish );

Here, the events have different types and are dispatched by different objects, but they all still trigger the same handler. Remember, though, that the handler is expecting the passed event to be of a certain class, so you can’t use the same function as a handler for both TimerEvents and MouseEvents unless its parameter is only expecting the base Event class.

{ 35 comments… read them below or add one }

Didds February 7, 2009 at 12:38 pm

Good tips :) Thanks

Ellison February 9, 2009 at 9:09 am

mind explaining why some could do this?
exampleTimer.addEventListener(“timer”,onTimerTick);

MichaelJWilliams February 9, 2009 at 10:58 am

Good question, Ellison.

It’s because TimerEvent.TIMER is actually a const (which is basically a variable that can only be changed at programming time, not once the Flash has been compiled) that is equal to “timer”.

We tend to use TimerEvent.TIMER rather than “timer” for two reasons:

  1. There’s a possibility that the programmer might change the value of TimerEvent.TIMER (from, say, “timer” to “timerTicked”), in which case all our code relying on it being “timer” would no longer work.
  2. A decent IDE like FlexBuilder, FlashDevelop or FDT will be able to popup a list of all events for a specific class — but only if they’re stored as consts like this.

Hope that helps

Almog Koren December 5, 2009 at 10:33 pm

I did a post on Tip and Tricks for event listeners this might help everyone
http://www.almogdesign.net/blog/actionscript-3-event-listeners-tips-tricks/

Michael Williams December 6, 2009 at 1:15 pm

Ooh, nice post! Thanks, Almog :)

Nils N. H. December 24, 2009 at 12:53 pm

This post made event listeners much easier to grasp. :)

Nirbhay December 28, 2009 at 7:25 am

very well explained.
thanks

Michael Williams December 29, 2009 at 1:16 am

Cheers, Nils and Nirbhay :)

David January 22, 2010 at 7:45 pm

I have a .fla template that I use to remove a video layer once the transparent greeter has played through. It was written is AS2. Thanks to the ease of dragging player controls onto the stage in AS3 I would like to replace this stop listener with identical AS3 code. I’m a virtual beginner with Flash CS3 and to date haven’t been able to accomplish it. This is the AS2 code:


import flash.external.*;

var fired:Boolean = false;

var stopListener:Object = new Object();
stopListener.stopped = function(eventObject:Object):Void
{
    trace("stopped");
    if (!fired)
    {
        ExternalInterface.call("onVidStop");
        fired = true;
    }
};
flvpb.addEventListener("stopped", stopListener);
</pre>

A commonjs.js file reacts to the stop and removes the layer. I don't know enough to know whether or not you'd need to see that to be sure of the same result, but I doubt it. If you can help in any way I'd be grateful.

David in Toronto

Michael Williams January 26, 2010 at 7:29 am

Hey David,

Your code looks like it would almost work in AS3 as it is! Let’s see… are you coding on the timeline or using AS files? Also, what exactly triggers the “stopped” event?

Ryan February 17, 2010 at 10:05 am

Hi Michael,

Thanks for the post; I’m just trying to come to grips with events in AS3. One problem I am facing is knowing when an event / eventhandler has been trigged inside of a class.

ie. this class performs some actions and once all external data is loaded, triggers an event

DatabaseConnection .as

public class DatabaseConnection {
...
loader.addEventListener(Event.COMPLETE,completeHandler);

private function completeHandler(event:Event):void {
trace("All data is loaded!");
}
...
}

Then in the main class I need to know when completeHandler has been triggered

Main .as

var DB:DatabaseConnection=new DatabaseConnection();

// accessing a property of DB returns null as it hasn't loaded yet...
// has the event completed in the DatabaseConnection class yet?

Any thoughts on how this can be achieved?
Cheers
Ryan

Michael Williams February 17, 2010 at 11:29 am

Hey Ryan,

You can do this by making DatabaseConnection dispatch an event of its own. You can create your own event class, but it’s probably easier if you clone the COMPLETE event that loader fires off:

private function completeHandler(event:Event):void {
   trace("All data is loaded!");
   dispatchEvent( event.clone() );
}

You can then add an event listener to your DatabaseConnection object for the COMPLETE event, and have the handler do whatever you want it to do. That should work.

…except it won’t, because DatabaseConnection doesn’t have the ability to dispatch events. The easiest way to get around this is to make it extend EventDispatcher (you’ll also need to import flash.events.EventDispatcher).

It’s not a perfect solution in all cases (you might want DatabaseConnection to extend something else, for example), but I think it’ll work here :)

David February 17, 2010 at 1:59 pm

Hey Michael, I apologize for disappearing after posting my question on stopListeners in AS3. To your question I’m coding on the timeline and it is the end of the video (FLVPB) that triggers the “stopped” event. For reference we’re going back to January 26th in this thread.

David

Ryan February 19, 2010 at 2:01 am

Thanks for the reply Michael.

I couldn’t seem to get the clone method to work – well at least the event wasn’t firing in the Main.as. In the end I created a new custom event and dispatched that from within DatabaseConnection.as and that seemed to work:

private function completeHandler(event:Event):void {
   trace("All data is loaded!");
   dispatchEvent( new Event(PHPConnection.DONE));}

Cheers
Ryan

ayu February 19, 2010 at 4:14 am

To Ryan:
Make sure that the addEventListener in your Main.as is listening to the same event object type as your clone.

For example:
If I were to dispatch a TimerEvent.TIMER_COMPLETE in my DatabaseConnection.as completeHandler( ), the Main.as should have an addEventListener listening to TimerEvent.TIMER_COMPLETE (it must be the same, else it will fail)

I have tested this (e.clone) and it works for me :D

if you want to try, below is a simple code:-

Main.as

package

{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;

public class Main extends Sprite 
{   
    public function Main():void 
    {
        var myTimer:MyTimer = new MyTimer();
        myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timerHandler);
        myTimer.start();
    }

    private function timerHandler(e:Event):void
    {
        trace("Event Clone");
    }
}

}

MyTimer.as

package
{ import flash.events.Event; import flash.events.EventDispatcher; import flash.events.TimerEvent; import flash.utils.Timer;

public class MyTimer extends EventDispatcher
{

    private var timer:Timer = new Timer(50,1);

    public function MyTimer() 
    {
        super();
        this.timer.addEventListener(TimerEvent.TIMER_COMPLETE, this.timerHandler);
    }

    public function start():void
    {
        this.timer.start();
    }

    private function timerHandler(e:TimerEvent):void
    {
        super.dispatchEvent(e.clone());
    }

}

}

Michael Williams February 22, 2010 at 9:11 pm

@David: Is any of the code working so far?

@Ryan: Did ayu’s reply help?

@ayu: Thanks for helping Ryan!

Ryan February 23, 2010 at 2:20 am

Hi Ayu,

I referenced ‘Event.COMPLETE’ in both classes and it did not seem to fire the event. Perhaps the event needs to be cast to an explicit type? I’m not sure. There could also have been another reason why the event was not firing.

I will try to play around with it when I get some more free time. For now the custom event is working =) Thanks Ayu and Michael.

ayu February 23, 2010 at 7:39 am

@Ryan

you can try placing a trace add the place where you are listening to the event (Main.as?)
If it traces xxx object, then you should listen to that xxx object.

javier February 27, 2010 at 10:24 pm

Hi, excellent to have complicated things explained so simple!

Have a question about the scoop of a variable, when handling EventListeneres:
How can fTwo access variable a accessible to fOne?

        function fOne() 
        {
            var a:int = 0;
            this.addEventListener(someEvent, fTwo);
        }

     function fTwo(e:Event):void
    {
        trace (a);
    }

may/ should/ can I use this instead? Is this valid? Is any better?

        function fOne() 
        {
            var a:int = 0;
            this.addEventListener(someEvent, fTwo);

        function fTwo(e:Event):void
         {
             trace (a);
          }
   }

The general idea is really to pass a second parameter to the fTwo…

thanks!
/j

ayumilove March 1, 2010 at 1:07 am

@javier

a function can’t access another function’s local variable.
this is because once the function has completed,
the local variable will be disposed (Not stored in memory)

one of the solution you can apply is to use
the class variable (public var / protected var / private var)

javier March 1, 2010 at 3:16 am

@ayumilove, thanks for your answer…

hmmm, I see now that my code was not as clear as i did see last night, with a lot of hours awake…

what I tried to say in the second example was if I can (or should, or shouldn’t) nest the function Two as a local function of function One – and so have access to f One’s local variable.

It is what I finally used, because in my simple example it was giving me the correct answers. But I really don’t know if in more complex situations the garbagecollector will destroy function One while function Two is still waiting for an event…
Do you know??

/br
/j

it should have looked like:

      function fOne() 
        {
            var a:int = 0;
            this.addEventListener(someEvent, fTwo);
            function fTwo(e:Event):void
              {
                    trace (a);
                 }
        }

ayumilove March 1, 2010 at 12:25 pm

@javier

hmm that I do not know,
but its not a good idea to have nested functions,
it makes thing complicated,

just declare two class functions,
functionOne adds the eventListener to the class itself
functionTwo handles the event once he catches that particular event occuring

if the class instance is nulled (not used anymore) with no other
class using that particular class instance,
then the garbage collector will come and clear it away.

also, you must use removeEventListener, it is because it will keep on
waiting and listen to that event unless you use removeEventListener.

rj2 March 8, 2010 at 3:40 am

hello, how would i simplify this i have a lot of mouse over and outs to do in this project
here is an example of two of them how would i simplify this code? maybe a conditional statement? im a little lost thanks

stop();
infoBox_mc.visible = false;
bergen_mc.addEventListener(MouseEvent.ROLL_OVER,b1);
bergen_mc.addEventListener(MouseEvent.ROLL_OUT,b2);
passiac_mc.addEventListener(MouseEvent.ROLL_OVER,p1);
passiac_mc.addEventListener(MouseEvent.ROLL_OUT,p2);
function b1(event:MouseEvent):void{
    infoBox_mc.info_txt.text = "Bergen County";
    infoBox_mc.visible = true;
    infoBox_mc.startDrag(true);}
function b2(event:MouseEvent):void{
    infoBox_mc.visible = false;}
function p1(event:MouseEvent):void{
    infoBox_mc.info_txt.text = "Passiac County";
    infoBox_mc.visible = true;
    infoBox_mc.startDrag(true);}
function p2(event:MouseEvent):void{
    infoBox_mc.visible = false;}

Michael Williams March 20, 2010 at 10:57 am

Hey, sorry it’s taken me so long to reply. Let’s see…

@Ryan: Glad to hear you have it working in some way :)

@ayumilove: Thanks for helping Javier as well ;)

@Javier: I am pretty sure that the “anonymous” function you’ve created will not be garbage collected, unless you use a weakly referenced listener. However, I’m not sure whether it’d be able to get the value of a. My guess is that it wouldn’t… let me know what you find out!

Michael Williams March 20, 2010 at 10:58 am

@rj2: How about this:

stop();
infoBox_mc.visible = false;
bergen_mc.addEventListener(MouseEvent.ROLL_OVER,b1);
bergen_mc.addEventListener(MouseEvent.ROLL_OUT,b2);
passiac_mc.addEventListener(MouseEvent.ROLL_OVER,b1);
passiac_mc.addEventListener(MouseEvent.ROLL_OUT,b2);
function b1(event:MouseEvent):void{
    infoBox_mc.info_txt.text = "Bergen County";
    infoBox_mc.visible = true;
    infoBox_mc.startDrag(true);}
function b2(event:MouseEvent):void{
    infoBox_mc.visible = false;}

javier March 22, 2010 at 2:51 pm

Michael,
I do get the value of a correctly from within functionTwo.

I don’t want the functionTwo from “disappear” while waiting (in my tests it does not disappear, and works correctly), but on the other side I don’t want the listener from being kept more than necessary…
So using weak reference to the listener does not seem what I want, but does the removeEventListener do it’s work? Or can’t I reference it like this? :

      function fOne() 
        {
            var a:int = 0;
            this.addEventListener(someEvent, fTwo);
            function fTwo(e:Event):void
              {
                    this.removeEventListener(someEvent, fTwo);
                    trace (a);
                 }
        }

thank you for all!
/j

Michael Williams March 22, 2010 at 3:37 pm

Hey Javier,

Oh! Well that’s interesting, thanks :)

Hmm, that might work. Give it a go, see if you get an error ;)

ayumilove April 6, 2010 at 4:09 pm

function fOne()
        {
            var a:int = 0;
            this.addEventListener(someEvent, fTwo);
            function fTwo(e:Event):void
              {
                    this.removeEventListener(someEvent, fTwo);
                    trace (a);
                 }
        }

i have checked, removeEventListener does not do its job.
why would you want to place it inside the function in the first place?
It’s bad coding.

Michael Williams April 13, 2010 at 4:29 pm

Oh. Well, good to know.

Sylvia August 19, 2010 at 6:07 pm

HI Michael,
I’m new to AS3, I’m wondering if you could please help me out.
I have an index.swf that loads a projects_list.xml file. Each node is like this.

projects_list.xml file set up


The index.swf loads the thumbnails for all the projects. When the user clicks on a thumbnail image I need it to open a new xml file with the details for each project.

desc_con.xml file set up (details file)


    <![CDATA[Project: Con
                   

Description of project here

]]> portfolio/images/con/full_size/logo_300pix.jpg portfolio/images/con/full_size/web_1_300pix.jpg portfolio/images/con/full_size/web_2_300pix.jpg

Here is the edited down version of my AS 3 code with just the snippets I thought you would need to see. I just can’t figure out how to pass the “link” tag of the current project_list node to the var Full_xml= in the CallFull function.

Many thanks
Sylvia

var myXMLLoader:URLLoader = new URLLoader();
myXMLLoader.load(new URLRequest("portfolio/xml/projects_list.xml"));
myXMLLoader.addEventListener(Event.COMPLETE, processXML);

function processXML(e:Event):void {
var myXML:XML = new XML(e.target.data);
my_project = myXML..project;
my_thumb_images = my_project.@thumb;
createContainer();
callThumbs();
}

function createContainer():void {
container_mc = new MovieClip();
container_mc.x = my_x;
container_mc.y = my_y;
addChild(container_mc);
container_mc.addEventListener(MouseEvent.CLICK, callFull);

}

function callThumbs():void {
for (var i:Number = 0; i < my_project_total; i++) {
var thumb_url= my_thumb_images[i];
var thumb_loader = new Loader();
thumb_loader.load(new URLRequest(thumb_url));
thumb_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoaded);
thumb_loader.name = i;
var thumbText:String = my_thumb_name[i].toString();
htmlThumbText= thumbText;
thumbName.htmlText = htmlThumbText;
container_mc.addChild(thumbName);

}

function thumbLoaded(e:Event):void {
//trace("thumb loaded");
var my_thumb:Loader = Loader(e.target.loader);
container_mc.addChild(my_thumb);
}

function callFull(e:Event):void {
//var Full_xml= "portfolio/xml/desc/desc_con.xml";//this works
var Full_xml= my_full_link;//works but it's loading all the xml files not the one associated with my thumbnail
trace("Full_xml= "+Full_xml);
var myXMLURL:URLRequest=new URLRequest(Full_xml);
var urlLoader=new URLLoader(myXMLURL);
urlLoader.load(new URLRequest(Full_xml));
urlLoader.addEventListener(Event.COMPLETE,parseDescXML);
}

function parseDescXML(e:Event):void {
var myDescXML:XML = new XML(e.target.name);
my_full_link = myDescXML.project;

}

Sylvia August 19, 2010 at 10:10 pm

I think I have found the solution.

I moved the event listener from the container_mc to the my_thumb

my_thumb.contentLoaderInfo.removeEventListener(Eve nt.COMPLETE, thumbLoaded); 

Then I used this code in the call_Full

var Full_xml= my_project[e.target.name].@link;

Many thanks for your help!

Michael Williams August 21, 2010 at 12:55 am

@Sylvia: Must admit, I didn’t really follow that, but glad to hear you fixed it :) Thanks for sharing your solution!

Randy May 3, 2011 at 7:16 am

Hello Michael Williams!
Thanks for your article, but I have 1 question on the same theme.
Can you explain why we use dispatchEvent sometimes?

Aditi March 29, 2012 at 10:19 am

import flash.utils.Timer;
import flash.events.TimerEvent;

var now: Date;
var ct:Timer = new Timer(1000);
ct.addEventListener(TimerEvent.TIMER, onTick);
ct.start();
public function onTick(event: TimerEvent): void {
now = new Date();
var s:uint = now.getSeconds();
var m:uint = now.getMinutes();
var h:uint = now.getHours();
secondHand_mc.rotation = 180 + (s * 6);
minuteHand_mc.rotation = 180 + (m * 6);
hourHand_mc.rotation = 180 + (h * 30) + (m * .5);
}

Hi

I had written the above code to show the clock, but this code does not work.

when i run the simple script it works fine but when I am trying to create the events it does not work at all.

can you please help me for this? are there any settings to be done for running the events in AS3

Leave a Comment

Writing code? Write <pre> at the start and </pre> at the end to keep it looking neat.

Anti-Spam Protection by WP-SpamFree

Previous post:

Next post: