Evolution of Event handling


on (Press)

When we need some action happen on button click, we select the button instance and do:

on (Press){
    //script here
}

The good thing is that, all things happen here. If something went wrong, we check this block. A pitfall exists. Each time we want to modify the action for the button click, we need to modify this block. 

We can not write action in other place. If this button is in a symbol in the library, we need script in the button of the symbol. All instances of this symbol will have completely the same action. Painful, right ? 

We can not change or modify this action dynamically. We can not disable this block dynamically.


onPress=function(){}

So, here is an evolution:

myButton.onPress=function(){
    //script here
}

We can script this line in _root frame or anywhere we feel comfort. We dont have to script in the symbol. We can assign different action to different instance of that symbol. We can re-define the button click action just re-define the myButton.onPress function.

We can even disable the button click function by saying "myButton.onPress=null;";

Now we can write and change the button click action anywhere , any time freely. We feel comfortable that if we create a button symbol, we can make each button different actions now. This is a marked improvement than on(Press).


Callback function: eventHandler

It is a trend to use movieClip as Button instead of the Button proper. When the movieClip is pressed, we need it look differently like we press Button proper to show the DOWN frame. So, onPress, we make it gotoAndStop(2) or script some graph by draw API. This internal thing should be done at the same time when our onPress action happens.

myButton.onPress=function(){
    this.gotoAndStop(2);
    //script action here
}

If we have 5 buttons, all these 5 buttons need to do the same thing. So the script for button2 will be :

myButton2.onPress=function(){
    this.gotoAndStop(2);
    //different script action for myButton2
}

All onPress functions of the button instances need to include that common line in addition to their unique actions. It is annoying. How do we know what lines we need to add in addition to our desired actioin ? If this button is created by our friend, we need him to tell us "dont forget to add a gotoAndStop(2) line" in each of your button onPress function !

OOP is a popular concepts. Now we talk about "symbol". It is advised that if possible, better not to include actions of "Outside" objects in the symbol. Also, it is advised that things about symbol itself be handled by script in this symbol not outside of the symbol.

So, the evolution of event handler is:

myButton.onPress=function(){
    this.gotoAndStop(2);
    this.onPressHandler():
}

OK, if I get this button movieclip symbol from my friend, I just need to script:

myButton.onPressHandler=function(){
    //script action here
}

myButton2.onPressHandler=function(){
    //different script action for myButton2
}

We need not care those "internal things belong to button itself". Those "internal things" is the job of the author of the button symbol, not consumers like us. So, it is a good thing to separate internal things off the click actions. 

In fact, most of the time we use them in-directly like this:

myButton.onPress=function(){
    this.gotoAndStop(2);
    this.onPressHandler():
}

mc.onClick=function(){
    //script here
}

myButton.onPressHandler=mc.onClick;

We say "onPressHandler" is a callback function. A function used by myButton.onPress but is opened to be defined or modified outside. The "mc.onClick" is the event handler for onPress function.

Becareful, passing the function reference to other object as a method has potential problem if the function contains "this" keyword. Lets see:

mc.onClick=function(){
    this._x+=20;
}

This function appears to move mc right ward on call.

Then we assign: myButton.onPressHandler=mc.onClick; When button is pressed, myButton.onPressHandler() takes place. It is "myButton" that moves rightward not mc. It effects like myButton.onClick();

So, the handler is better without the keyword "this". It is better to write the event handler function as a "function" not a "method".

A thing needs brief mention. In this example, our internal onPressHandler and external onClick function contain no parameters. We can also make them functions with parameters. Those parameters can convey some extra-messages that button wants to give out. But, this is not very pratical if this function is called by the button itself. We will see this technique in the section below when callback function is executed by outside object rather than button itself. We will see how buttons send these extra message to outside object by this parameter.


AsBroadcaster : turn action into array

Well, it is too bad to have a restriction that, handler can not have "this" keyword. Why not includes the object as "this object" in event handler, so it is mc.onClick rather than myButton.onClick ?

If I have two movieClips, and they need to take actions when myButton is clicked. Can I say: myButton.onPressHandler=mc1.onClick+mc2.onClick ? Obviously, this syntax wont work.

If I dynamically create many clips, can I say: myButton.onPress.push(newMc.onClick); ? Obviously, we can't;

Well, there is only single one callback function. We have named it to be onPressHandler. We can not "add" any action to it;

In old days, we like to create onEnterFrame function in each movieClip and check a global variable "myButtonPressed". If it gets true, we assume that button is pressed and each movieClip will do responce by taking reference of this global variable. This is not a good idea.

Here is the "AsBroadcaster" model of event handling . We can add some action or remove some action flexibly.

We make the button a Broadcaster. The button harbors a list of Listener. When the button get clicked, it broadcasts the "onClick" event to those object in the listener List. Those object in the List will take action on click event.

This solves the questions we mentioned above. The solution is just push mc1 and mc2 to the listener array , so the listener array will be [mc1,mc2]. Then, on click, it will execute mc1.onClick and mc2.onClick. If we dynamically creates an object, we push this object into the listener array and the listener array will be [mc1, mc2, obj] and obj.onClick will be executed.

In other words, myButton.onPress excutes:

for(i=0;i<listenerList.length;i++){
    listenerList[i].onClick(event);
}

So, it is nothing mysterious or magic to the broadcaster model. Instead of passing an event handler to buttons, we pass an object to buttons.

Since we can add our object into the listener list, we can also remove our object from the listener list.

I dont want to detail the syntax how to code broadcaster model. I include an MX fla here.

You may wonder whether we have to name those functions as"onClick" ? This is a message name. Officially, we say it is event.type. If we want to name our message "pressed" instead of "onClick", then we broadcastMessage("pressed", param); And mc.pressed() function instead of mc.onClick() will be executed.

The param is an extra-message. We can convey any data in it. It is handly to make an event object as the param, and this param will be sent to event handler as parameter: mc.onClick(param);

Here is a snipet of codes.Please see that fla (broadcaster.zip) I included.

For the broadcaster: blueButton:

ASBroadcaster.initialize(blueButton);
blueButton.onPress = function() {
    this.gotoAndStop(3);
    var event = {target:this};
    this.broadcastMessage("pressed", event);
};

For the listener: mc1:

mc1.pressed = function(event) {
    this.txt.text = event.target._name+"  is pressed";
};
blueButton.addListener(mc1);

Personally, I dont like the term: "broadCast" and "Listerner". The term "BroadCast" give me an impression that broadcaster just dispatches the sound without knowing who are listening. In fact, the broadcaster has a list. The "broadCaster" just contact with those members in the list like "whisper", not broadcast to the air. The term "Listerner" give me an impression that the listerner pays attention and repeatedly monitor the broadcaster so that it might not miss the message. In fact, the listener did nothing but "register" its name to the list. It does not "listen". It might well sleep. It is the broadcaster that will wake him up and whisper to him.

Ok, I have a question: We push _root, mc and myObject into the listener list. When the message "onClick" is broadcasted, which one will be executed first ? _root.onClick ? or mc.onClick ?

It depends on which one gets the booking first. If we addListener(mc) before addListener(_root), then mc.onClick goes first. If result of mc.onClick depends on the result of _root.onClick, then things will be messed up.

OK, it is perfect now. we can esily extend the action outside of the button symbol. We can convey additional data such as button instance of button name in extra-message parameter and handle it in our event handler. What else ?

that fla (broadcaster.zip)


EventDispatcher:

In java, event is an object. It stores the event type as "onPress"....etc. It also stores "target", the object that gives out the event. (Personally, I think it is event.sender rather than event.target). To simulate this model of event handling, Macromedia writes an EventDispatcher class. This is not a native or internal event handling model. It is custom made event handling Class and functions. We can even modify that code and #include it to Flash 5 file to use this model. The detail can be obtained by viewing the mx.event.EventDispather.as;

Briefly, it is similar to AsBroadcaster model. In fact, I dont see any thing superior to the internal AsBroadcaster model except the compatibility with JAVA language.

The name of those methods are different. broadcastMessage turned to dispatchEvent, addListener turned to be addEventListener etc.

BroadCaster owns a single listener array stores the listeners. All event are sent to listeners in this listener array. "onPress" check this listener array. "onRelease" also check the same listener array. So, addListener contains only one parameter.

EventDispatcher owns several listener array. One listener array for one event. For example, for "click" , EventDispatcher has a "__q__click" array, stores the listener which will get "click" event dispatched. When "dragOut" event needs to be dispatched, the EventDispatcher check another listener array, the "__q__dragOut" array. "q" means "queue". So, in broadcaster model, we do addListener(listener), while in EventDispatcher model, we do addEventListener("eventType",listerner);

In broadcaster model, it broadcastMessage(eventType:String, extraMessage:String); We usually pass the name of event.target through the extraMessage . So the listener.click(extraMessage); We check the extraMessage to get the target. In EventDispatcher model, we make an event object as {type:"click",target:this); then dispatchEvent(event); So, the listener will do "click(event);" We check event.target to get the target.

Since event stores not only the target , it also stores the event type, so we need not do "click(event);" and "dragOut(event);" We can simple write "handleEvent(event);". We can retrieve the type from event.type; This can be implement by broadcaster model too.

var event={type:"pressed",target:this}
broadcastMessage("handleEvent", event);

EventDispatcher model also allow a function to be a listener.

Another potential benifit of event being an object is that we can pass even more extra data in the event object. For example, event={type:"click",target:this, data=username); This extra data can be accessed in click handler function.

So, should I choose EventDispatcher or AsBroadcaster ? Either way will work. However, if we use Macromedia components, they are already initialized as EventDispatcher, we can easily addEventListener to take the event. Everyone would believe that, addEventListener is the standard way when we use components. So, it may be good to adopt this whether you are going to write UIComponent or not.

I dont want to detail the syntax about EventDispatcher. I include an MX 2004 example fla here .

Here only some sniplets:

For the EventDispatcher:

import mx.events.EventDispatcher;
EventDispatcher.initialize(blueButton);
blueButton.onPress = function() {
    this.gotoAndStop(3);
    var event = {type:"pressed", target:this};
    this.dispatchEvent(event);
};

For the EventListener:

mc1.pressed = function(event) {
    this.txt.text = event.target._name+" is pressed";
};

blueButton.addEventListener("pressed", mc1);

        There are other kind of listener. Please check dispatcher.zip the fla.


What is event ?

Events means we can not predict the time when things will happen, We can not predict when user will press the button. We dont know when user will press the key.

"onPress","onMouseDown" are events. "onKeyDown" is an event that need addListener. We are familiar with these. There is no "lower" level codes that can replace these system events.

How about custom events ? Custom events are not necessary. But, sometimes it make script more structural and concepts clearer.

For example:" if my content of input textbox changed, my neon-light clip should update the appearance";

We dont know when the input textbox will get changed. So, we script:

textbox.watch(text, neonLight.update);

So neonLight.update is a callback which will be executed when textbox,text is changed.

We dont know when the input textbox will get changed, so it kind of an event. Instead of writing all the action in one single callback function, we take an object as broadcaster or event dispatcher and make this object broadcastMessage or dispatchEvent in the callback function. Make neonLight listen to this broadcaster and do update things.

It appears that we have gone around an un-necessary long way. Yes, I agree with this point. But we find its benefits in the easiness to add or remove action dynamically. Once we add another neonLight. We can easily add thie new neonLight to listener list to make it responsive to this event. It works handy if we are going to create many many neonLights dynamically.

The second example of custom event is : loadMovie; We dont know when movie is completely loaded. So, we say

if(getBytesLoaded()==getBytesTotal()) doMovieManipulation();

Yes, we can make all our actions in that doMovieManipulation function. Instead, we can make it an event;

    if(getBytesLoaded()==getBytesTotal()) broadcastMessage("alreadyLoaded",movieName);

It is very easy to add or remove the responce of another movieClip. It is handy when the responsive clip is created dynamically.


Listen and watch

I see many get confused about the difference between Listening and watching. Can we listen to the change of properties ? Can we watch an event ?

To put it straightly: The Watcher dispatches the event to the Listener.

Object can only watch properties that belong to the object itself. The target is "property name", a string. It can watch many properties but can not watch the properties of other objects. 

When the watched property gets set , a callback procedure is called. We can make this callback procedure to create an event and notify the Listeners by broadcastMessage or dispatchEvent. 

Listener sleeps all days. It gets notified by the dispatcher. It is told to react. Basically, it does not know or care why that event occurs. It just do its job.

So, the watcher detects the change. The callback procedure creates and dispatches the event. The Listener reacts.

For simple project, we detect the change and do reaction. For complex project, we detect the change, then create an event, then dispatch the event to listener. Listeners do the reaction.


email: ericlin@ms1.hinet.net