Removing Anonymous Event Listeners
One of the features that I really like about AS3 is its support of closures. There are many instances when you want to do something to an argument but don't really need or want a full blown class to encapsulate the logic. Lately I've been trying to apply this approach to event listeners and in general it just works. My problem can up when I wanted to have the event listener run only once or stop listening after some point in time. Since the removeEventListener method requires a reference to the listener that you want to remove and the listener in this case is a closure I need a way to get a reference to it. Thankfully closures provide a way to do that but it requires introducing another variable into the mix. Thankfully ActionScript provides another way to get access to the same information arguments.callee.
Arguments is a variable available in every function that is automatically supplied by the system. It is the older style of getting access to the arguments of a function that takes in an arbitrary number. In this case though the callee also gives you access to yourself. In the case of an anonymous event listener, this is all that is needed to remove it as an event listener. Now for the example:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="handleCreationComplete();"> <mx:Script> <![CDATA[ private function handleCreationComplete():void { sampleButton.addEventListener(MouseEvent.CLICK, createClickListener(1)); sampleButton.addEventListener(MouseEvent.CLICK, createClickListener(2)); } private function createClickListener(clickLimit:int):Function { var clickCount:int = 0; return function(mouseEvent:MouseEvent):void { clickCount++; trace("Click limit", clickLimit, "registering click", clickCount); if (clickCount >= clickLimit) { trace("Click limit", clickLimit, "no longer listening"); mouseEvent.target.removeEventListener(MouseEvent.CLICK, arguments.callee); } } } ]]> </mx:Script> <mx:Button id="sampleButton" label="Go"/> </mx:Application>
That gives this output:
Click limit 1 registering click 1 Click limit 1 no longer listening Click limit 2 registering click 1 Click limit 2 registering click 2 Click limit 2 no longer listening
In this example I'm programmatically adding two anonymous event listeners to my button class. They are anonymous in the fact that the function being executed isn't named. Unlike the creationComplete listener that is attached to the Application, you can only pass around the reference to the function, instead of naming it directly. As I mentioned above, since I want to remove the event listener I can't name it directly. Also since I'm creating multiple instances of it, I can't easily pass the reference to the function around. By instead using arguments.calee I avoid the need to name the function and the need to pass the function reference around.
I'll admit that this is not the most compelling example usage of this technique, but I'm sure you can extrapolate from this.
Comments
Posted by: paul | January 5, 2007 9:33 PM
Posted by: Gregory Golberg | January 12, 2007 9:49 PM
Posted by: DanielR | January 12, 2007 11:14 PM
Posted by: Amarghosh | March 3, 2008 8:00 AM
Posted by: Victor | April 29, 2008 9:55 AM
Victor:
I presume that the call to m.removeEventListener() happens somewhere else in the code. In that case this technique doesn't work since you somehow need to pass the reference across space. If where you add the listener and where you remove it share some state you could try something like:
Posted by: DanielR | May 6, 2008 9:59 PM
Posted by: forsvunnet | September 12, 2010 9:34 AM