One of the questions about ActionScript 3 that I am most often asked is how to send arguments to a listener function along with an event. Recently, a reader named Jim asked just such a question.
To provide some background for this question, you could send argument data to a standard function like this?
function showMsg(msg:String):void {
trace(msg);
}
showMsg("Claire");
However, in an event listener, only one argument is allowed in the listener function: the argument responsible for receiving the event data. Using a standard mouse click listener as an example...
stage.addEventListener(MouseEvent.CLICK, showMsg, false, 0, true);
function showMsg(evt:MouseEvent):void {
trace("hello");
}
...the kind of question asked is, can you do something like this:
stage.addEventListener(MouseEvent.CLICK, showMsg, "hello",
false, 0, true);
function showMsg(evt:MouseEvent, msg:String):void {
trace(msg);
}
The answer is, not out of the box. The existing AS3 events do not provide for this capability. However, to answer Jim's question, the best way to pass arguments with an event is to create your own event class by extending AS3's Event class.
No-Class Alternatives
Before we get to the custom class, let's look at a few lesser approaches for using dynamic data with events. In the following examples, I'll take advantage of properties from an event, along with dynamic data, to show the use of both kinds of information. A MouseEvent instance includes a Boolean called shiftKey that indicates whether or not the shift key is pressed at the time the event is dispatched.
Note: Any time a trace() method is used to trace an empty string or new line, it is only used to visually differentiate one trace from another in the Output panel.
The first approach is to populate a variable and then use that variable inside the listener function. This is straightforward, but not very dynamic:
var msg:String = "Shift key was down:";
stage.addEventListener(MouseEvent.CLICK, doIt, false, 0, true);
function doIt(evt:MouseEvent) {
trace(msg, evt.shiftKey);
trace("");
trace(evt);
}
The second approach is to use a standard event to call an interim function that then calls the desired function, passing along any relevant information. In my opinion, this is the best out-of-the-box solution, but it is also more verbose:
stage.addEventListener(MouseEvent.CLICK, doIt, false, 0, true);
function doIt(evt:MouseEvent) {
doIt2("Shift key was down:", evt);
}
function doIt2(msg:String, evt:*) {
trace(msg, evt.shiftKey);
trace("");
trace(evt);
}
The last approach I'll discuss in this post is to use an anonymous function within the addEventListener() method under the guise of elminating the interim function in the previous example. This is a bit ugly and problematic:
stage.addEventListener(MouseEvent.CLICK,
function(evt:MouseEvent){doIt(evt, "Shift key was down:")},
false, 0, true);
function doIt(evt:MouseEvent, msg:String) {
trace(msg, evt.shiftKey);
trace("");
trace(evt);
}
Looking at this format objectively, it's not really any less verbose, it's just crammed into one line. It's also harder to read—particularly when the anonymous function requires more than one line to execute the desired functionality. Most importantly, however, anonymous functions can be a pain to work with and are hard (impossible?) to clean up after. With AS3's memory management requirements, this is a headache you just don't need.
Creating a Custom Event Class
For our needs, we'll create a custom event class by extending Event. This gives us all the advantages of the parent class, including dispatching. Here is a basic example that I'll explain bit by bit:
-
package {
-
-
import flash.events.Event;
-
-
public class CustomEvent extends Event {
-
-
public static const CUSTOM:String = "custom";
-
-
public var arg:*;
-
-
public function CustomEvent(type:String, customArg:*=null,
-
bubbles:Boolean=false,
-
cancelable:Boolean=false) {
-
-
super(type, bubbles, cancelable);
-
-
this.arg = customArg;
-
-
}
-
-
public override function clone():Event {
-
return new CustomEvent(type, arg, bubbles, cancelable);
-
}
-
-
public override function toString():String {
-
return formatToString("CustomEvent", "type", "arg",
-
"bubbles", "cancelable", "eventPhase");
-
}
-
-
}
-
-
}
The class begins with the package declaration, and an import statement to tell the compiler where to find the Event class. The class declaration follows, extending Event.
The next line declares a public static constant, CUSTOM. This is the constant that will allow a scripter to specify this event, the same way you use CLICK to declare a click event of the MouseEvent class. These constants are strings and it's best to specify something that will not conflict with another event string assignment (and, therefore, possibly trigger another event listener).
Next is a public property for any data you want to send with the event, such as the aforementioned shiftKey property found in the MouseEvent class. In this simple case, the value is typed using the asterisk, which will prevent type checking allowing any data type to be used.
It is largely a matter of preference whether you prefer to use public variables or getters and setters for accessing properties from outside a class. Many OOP advocates will insist that getters and setters are the only way to go. Others maintain that "headless/brainless" getters and setters (functions that don't add any functionality, such as error checking, casting, or other manipulation) offer little value.
I leave it to you to decide. In the downloadable source files, I've provided two versions of the next custom event class discussed in this post ('a practical example') with which you can experiment. You need only swap the classes and everything will work the same way, regardless of which approach you prefer.
If this doesn't mean anything to you, just stick with the version used in the examples (that uses public properties) until you learn more about getters and setters and determine your own personal preference on the issue. You can start looking into the idea with Chapter 6 of the book (OOP), which briefly demonstrates getters and setters in the Encapsulation and Composition sections.
The class constructor begins on line 11 and is very simple. It accepts as parameters, the event type, the custom argument, and the ability to bubble or cancel the event (the latter two, false by default). Only the custom argument is different from a standard event. It is placed second in the list of parameters so you can omit the last few parameters from the event dispatch. Their default values will be used if they are omitted. Similarly, if no new custom data is provided, it's default value of null will be used. If the custom argument was placed at the end of the list (a logical place for new information), you would have to include values for all the optional parameters in the correct order, just to change the custom data argument.
The next line calls the super class to reap all of its benefits, passing along the type, bubbles and cancelable setting. Note that the argument is not passed on here because it's not a feature of the extended Event
Additional Features
Theoretically, you can stop here but there are two important features that you should also include to make your class feature-complete.
clone()
On Line 21, our custom class overrides the clone() method of the parent class. This is necessary to insure that your event will behave properly if you ever need to redispatch it. The clone() method is called automatically for a built-in class but, when extending a class, you must account for the new features of your class and override the original method. Without including this override, none of the event attributes will carry through and you will receive an error. The source code that accompanies this post includes an example of redispatching the event. You can try the file with and without the function override to see how this might impact your project.
toString()
Finally, an "undersung" hero of the Event class appears on line 25. If you override the toString() method as well, including your class name, custom argument, and original event attributes (type, bubbles, and cancelable), your custom class info will appear in any trace of the event. This is handy for quick references to property names. For example, if you don't override the method, and trace the event with a custom argument like this:
trace(new CustomEvent(CustomEvent.CUSTOM, "Claire"));
...you'll get the following:
[Event type="custom" bubbles=false cancelable=false eventPhase=2]
All you know is that an Event of type "custom" was dispatched. However, if you override the method as shown, you get this:
[CustomEvent type="custom" arg="Claire" bubbles=false cancelable=false
eventPhase=2]
Not only do you know that you're working with a CustomEvent, you also see the custom value you've added (arg="Claire").
Using the Custom Event
You can use the custom event like this, which will trace both the event and the custom argument that you send with the event:
this.addEventListener(CustomEvent.CUSTOM, onCustom, false, 0, true);
function onCustom(evt:CustomEvent):void {
trace(evt);
trace(evt.arg);
}
dispatchEvent(new CustomEvent(CustomEvent.CUSTOM, "Claire"));
A Practical Example
Now let's look at a custom class at work. The following application is a very basic simulation of registering a user for something. We'll be using a custom event class, UserRegistrationEvent. The interface requires a user name and age, and an optional employment status can also be provided. Upon an attempt to register, basic error-checking is performed. If the name field is empty, or the age provided is less than 18, a custom error event USER_REGISTRATION_ERROR, is dispatched, and a warning string is placed into the Result field. Otherwise, a custom complete event, USER_REGISTRATION_COMPLETE, is dispatched and each of the custom values carried with the event is placed into the field. For demo purposes, the full event is added to the field in each case.
Here is the custom event class used. The only significant differences between this example and the earlier custom event class are the fact that UserRegistrationEvent has two event constants (instead of one), and three custom values (instead of one).
-
package {
-
-
import flash.events.Event;
-
-
public class UserRegistrationEvent extends Event {
-
-
public static const USER_REGISTRATION_COMPLETE:String
-
= "userRegistrationComplete";
-
public static const USER_REGISTRATION_ERROR:String
-
= "userRegistrationError";
-
-
public var name:String = "";
-
public var age:int = 0;
-
public var employed:Boolean = false;
-
-
public function UserRegistrationEvent(type:String,
-
name:String="",
-
age:int=0,
-
employed:Boolean=false,
-
bubbles:Boolean=false,
-
cancelable:Boolean=false) {
-
-
super(type, bubbles, cancelable);
-
this.name = name;
-
this.age = age;
-
this.employed = employed;
-
-
}
-
-
public override function clone():Event {
-
return new UserRegistrationEvent(type, name, age, employed,
-
bubbles, cancelable);
-
}
-
-
public override function toString():String {
-
return formatToString("UserRegistrationEvent", "type",
-
"name", "age", "employed",
-
"bubbles", "cancelable", "eventPhase");
-
}
-
-
}
-
-
}
And here is the relevant code that checks the form input and dispatches the appropriate event (with explanations to follow the script):
-
//form button listener
-
regBtn.addEventListener(MouseEvent.CLICK, onReg, false, 0, true);
-
function onReg(evt:MouseEvent):void {
-
var userAgeInt:int = int(userAge.text)
-
if (userName.text != "" && userAgeInt> 18) {
-
dispatchEvent(new UserRegistrationEvent(
-
UserRegistrationEvent.USER_REGISTRATION_COMPLETE,
-
userName.text, userAgeInt, userEmployed.check.visible));
-
} else {
-
dispatchEvent(new UserRegistrationEvent(
-
UserRegistrationEvent.USER_REGISTRATION_ERROR));
-
}
-
}
-
-
//reg complete listener
-
this.addEventListener(UserRegistrationEvent.USER_REGISTRATION_COMPLETE,
-
onRegComplete, false, 0, true);
-
function onRegComplete(evt:UserRegistrationEvent):void {
-
regResult.text = evt.name + "\n";
-
regResult.appendText(String(evt.age) + "\n");
-
regResult.appendText(String(evt.employed))
-
-
regResult.appendText("\n\n");
-
regResult.appendText(String(evt));
-
}
-
-
//reg error listener
-
this.addEventListener(UserRegistrationEvent.USER_REGISTRATION_ERROR,
-
onRegError, false, 0, true);
-
function onRegError(evt:UserRegistrationEvent):void {
-
regResult.text = "Error during registration.
-
Please check form input and try again.";
-
-
regResult.appendText("\n\n");
-
regResult.appendText(String(evt));
-
}
Lines 2 through 13 make up the listener for the Registration button in the form. The function converts the age text submission to an integer and checks to see if the age is over 18 and the name field is not empty. If these two tests pass, a registration complete event is dispatched, sending the name, age, and employment status with the event. If the tests fail, a registration error event is dispatched.
Lines 16 through 25 comprise the listener for the registration complete event. It places four pieces of information into the text field: the user's name, age, and employment status, followed by the entire event for illustration purposes. (Line feeds are added along the way for clarity.)
Lines 28 through 36 contain the listener for the registration error event. It replaces the contents of the Result field with an error message and the complete event.
Note: The source code places the data in the text field but traces the event to the Output panel for additional separation of functionality. It also includes additional code to control the checkbox, which is irrelevant to this tutorial.
Example Source Code
Download Passing Arguments with Events (40.8 KB, 2,772 hits)
The source code that accompanies this post contains several files. Flash users should start with a set of FLA files (saved in CS3 format for maximum compatibility), including the example shown herein. In this set are a simple proof of concept, the basic UI example above, and a UI example that shows the importance of using the clone() method when redispatching events.
I've also included a set of class-only files, moving the FLA-based material to a class so users of other coding environments can take advantage of the examples. Furthermore, the classes can be used as document classes by Flash users, if preferred. Both the single dispatch and redispatch examples are included.
Finally, two versions of the custom event have been included, as explained earlier in the post. The version used with the example uses public properties for simplicity, but another version using getters and setters has also been provided. If you are a fan of the latter, you can simply swap out the class and everything will work the same way.
Print This Post


(45 votes, average: 4.49 out of 5) 



Thanks for posting a solution to this issue. It's these simple problems that seemed to get overlooked in other discussions about OOP in Flash.
Thanks, @Alex.
Hi there, love your book, it's been making the transition from AS2 so much easier. I have implemented this solution and it works, but after the arg trace, I get this error
TypeError: Error #1034: Type Coercion failed: cannot convert flash.events::Event@1c87ba61 to com.ro.CustomEvent.
ideas?
Thanks again
@happaGirl, this menas that you're sending an Event data type to something that is typed as CustomEvent. For example, in the example code above, you may have typed the argument as CustomEvent but dispatched an Event.
The CustomEvent class is a skeleton that is meant to be adapted to your needs, including optionally renaming the class and event names. Try to compare your use of the class to the sample code both in the CustomEvent example and the UserRegistration example to see if you can spot where the coercion is happening. Usually, you'll get a line number with this error.
This is great for larger "engineering" projects or libraries, but it's actually a lot more work than just sending in a variable as an argument. You have to create a whole class file, structure it properly (using class inheritance, overriding the right properties, etc.), and/or you have to create a series of functions, instead of just the one that will take your data the way you want it to.
I'd really like to see Adobe add back some of the easy to hack features they used to have in the AS1.0 and AS2.0 days.
Maybe even add in some new JS2.0(-like) features, like structural or duck typing.
That'd be awesome.
@Kevin, yes, many agree. What you've written is certainly true of the "practical example" in this post. What I sometimes use is a more generic event replacement class--along the lines of the skeletal CustomEvent example--that can accommodate additional data. I tend to use an object so I can throw anything in there, but without doing more, that doesn't give you the benefit of data type checking.
I have worked to convince myself that hacky ways of doing things lead to trouble, and embraced the stricter approaches. There's no denying, however, that it requires more work and a steeper learning curve.
Hi Rich,
I have a different solution, and wanted to know what you thought. I teach a lot of Flash, so developing a simple way to be effective is always my goal. By nesting the event inside a function, I can pass multiple variables easily. Here is a really simple example:
// create function with variables to be passed to it. function isBtn(myTarget:Object, myMessage:String) { // functions function btnClick(evt:MouseEvent) { trace("click: " + evt.target.name); trace(myMessage); } // events myTarget.addEventListener(MouseEvent.CLICK,btnClick); } // creating buttons and passing simple strings isBtn(btn1, "hello1"); isBtn(btn2, "hello2"); isBtn(btn3, "hello3"); isBtn(btn4, "hello4");Simple, but it works. I am not passing variables through the event, but rather through the function.
@Jonathan, thanks. There are, of course, many solutions to this issue. The main thrust behind this post was to demonstrate a way of accomplishing the described goal by creating a custom event class. I'm glad your approach was added to this thread, and keep on contributing!
Hi Rich.
I just wanted to say thanks for sharing this. It was a very good example and very clearly explained. I have attempted to understand the dispatch event and event listener a few times, but this really helped. I also like the fact that you can pass arguments. Good stuff!
Vayu
@Vayu, thanks!
@Jonathan. Thank you for your solution. It is so much easier and exactly what I was looking for. The example that started this thread does not let you do what I was looking for. I have several buttons which, based on the one that is clicked should show different images. So I needed a way to get a command argument for each button. Your solution provides this in the simplest way. Kudos.
super useful, been wanting to do this, and unfortunately I've been choosing every verbose over complicated way possible until now! thanks a lot!
Jonathan, bingo. Been searching all over the web for this.
Finally a real solution. I've been searching and posting on how to do this. Thanks!
Rich, regarding your 2nd Dec '08 reply (see above) and your comment "I sometimes use a more generic event replacement class...that can accommodate additional data...like an Object" (or an Array!?); dude, if you have a generic Class which can do this and be reused, could you post it please, please, please! I have a working version but it requires a unique Class for each custom event =p After all, OOP is suposed to be reusable (that's what the Adobe Marketing Dept says so it must be true).
AS2 EventDispatcher provided this ability natively and I really miss it.
Thanks!
Rich –
Regarding my May 21st, 2009 comment (see above); after much thrashing about, I've come to the conclusion that trying to recreate my old AS2 OOP architecture was the wrong approach--specifically, how I pass events and data (arrays & objects, mostly) between classes. Better is to let AS3 do its thing in its own way. The better solution I’ve come to is to simply use getters & setters in conjunction with standard and custom events. Learning both AS3 and OOP at the same time has been a challenge but, now well along, I find it easier than I expected (hindsight seems to work that way). Your book, and your additional postings here, have provided me with a great AS3 jumpstart and continued resource without which I would not be where I am today.
Thanks again.
Hi. Thank you for these turotials, they are very thorough and well laid out. I can't seem to make the Custom Event class to work properly in my testproject though. This is a project where I try to get to grips with deeplinking with SWFAddress. I have tested the relevant issues and SWFAddress seem to be working as intended.
I have a main.swf which loads a numberOne.swf when I click a button. The numberOne.swf in turn loads a numerOneB.swf when the appropriate button is clicked. When I click a button in numberOneB the SWFAddress value is set to for instance "/NumberOne?page=video". The listener for the SWFAddresssEvent.CHANGE in main.swf trigger a function that checks the value of SWFAddress. If the value is "/NumberOne?page=video" it dispatches an event using my new custom class from main.swf with an argument (a string).
I also have a listener in the constructor in the document class of numberOne.swf which listens for dispatches through my custom event class and then trigger a function. This function currently just writes to a Textfield in main.swf but it is meant to load the appropriate movieClips or .swf's depending on which argument is passed.
The custom class seem to be set up correctly; I get no errors, and it works as expected if i put the listener and its function inside main.swf. But I get no action when I put the listener inside numberOne.swf. I get the correct SWFAddress-value passed from numberOne.swf to main.swf, but I don't get the argument from the custom event class.
Can you see any obvious fault in this outline. The whole thing is laid out on http://nrksuper.no/superstore/flashfiles/sigtest/dyplenking/ so you can see that it works (somehow) if you like. The file names are in Norwegian so they don't totally correspond with the names I've used above.
I hope this isn't taking too much of your time
Kind regards
Sigmund Øvrum
Wow - I'm terribly SORRY!!! I just found out what I did wrong. I have to write it like this: myLoader.content.dispatchEvent(new MyCustomEvent( MyCustomEvent.NEW_PAGE, "video"();
I did, and now it works.
Thanks anyway, for reading all this shit.
Yours humbled
Sigmund
I found the article valuable for explaining how to dispatch custom events. That certainly seems easier in AS3 than AS2 but in any case very useful. I agree that the example is one that would be hard to justify unless there was some serious scaling issues. For a simple Flash movie, the simpler approaches would be awful appealing. The one thing that I'd like to see is an explanation how to extend the button class so that I could do something like follow:
regBtn.addEventListener(MouseEvent.EXTENDED_CLICK, onReg, "Claire", false, 0, true);
This apppoach might be a lot of work behind the scenes to setup but thereafter, I could use the EXTENDED_CLICK event whenever I have a button.
@John, can you clarify a bit? You wouldn't extend the button class to accomplish what you're describing. You seem to be describing something like this: First, add an event called EXTENDED_CLICK to the MouseEvent class. Next add a String (or untyped) parameter to that class so you can pass data along with the event. Last, make the new event dispatch via the mouse? Does that sound right?
Hello. This was a great article. I was googling for this, i think for months but i couldn't find exactly what i wanted. Passing arquments with custom events is so helpfull and handy in designing websites.
Thank you.
@Rich, thanks for that tip. I spent some time looking at MouseEvent and I see that is the avenue I would need to go down. The crux of my point is that adding a dispatch statement to a callback method requires that work everytime. I was inquiring into solutions that are more general and that a developer could setup once and use over and over. I will look into this some more and maybe post back.
Here is an approach I use when I want to pass arguments with events. It's kind of similar to Jonathon's, but I thought it worth posting. I find this useful for testing things and if I am in a rush and various other times!
Example 1.
This is when I want to bind activity to one particular event of one display object:
btn.addEventListener(MouseEvent.CLICK, tempfn) function tempfn(e:Event):void { trace('btn title') }Example 2.
If you want to do this for more than one object..
Use a literal function (I think that's the right term). Any road, in the example below, when the user clicks on 'btn', or 'btn2', the statements within the function literal tempfn - declared as the function for the event - will fire.
btn.addEventListener(MouseEvent.CLICK, function tempfn(e:Event):void{ trace('btn title') } ) btn2.addEventListener(MouseEvent.CLICK, function tempfn(e:Event):void{ trace('btn2 title') } )So if you replace the body of tempfn with functions to update a text field or load an image and pass params to these functions like the example below...you have a way of getting events to do bespoke things quickly.
btn.addEventListener(MouseEvent.CLICK, function tempfn(e:Event):void{ updateTf('btn title'), loadImage('imageName.jpg') } )@Tino, thanks for posting!