Streamlining your AS3 code

This tutorial is going to be a very simple (and quick) one. Using a very simple example, it will show you techniques for combining event listeners, and using arrays and loops to process your objects (MovieClips).

Brute force programming

First, download the example file above. It just contains a few simple objects: a square, a circle, and a triangle. These are MovieClip instances. They are very simple, single frame MovieClips. I like to do a lot of experimenting with such objects, because for one thing, they are fast and easy to create. For another thing, though, you realize that, programming-wise, the things that work for simple objects also work for complex ones.

We are programming our three objects using the brute force method. The three objects on the stage have instance names of square, circle, and triangle. The buttonMode is set to true, individually for each object. Next, we are also writing an event listener for a MouseEvent.CLICK event for each object individually. Each event handler function is written with a specific object in mind, there's nothing anonymous going on at all. When you click each item, a hand-tailored message is displayed in a dynamic textbox, letting you know which item you clicked. Here's how the swf behaves:

And here is the brute force code that makes it happen:

import flash.events.MouseEvent;

square.buttonMode = true;
circle.buttonMode = true;
triangle.buttonMode = true;

square.addEventListener(MouseEvent.CLICK, square_onClick);
circle.addEventListener(MouseEvent.CLICK, circle_onClick);
triangle.addEventListener(MouseEvent.CLICK, triangle_onClick);

function square_onClick(event:MouseEvent):void {
	myText.text = "square was clicked";
}
function circle_onClick(event:MouseEvent):void {
	myText.text = "circle was clicked";
}
function triangle_onClick(event:MouseEvent):void {
	myText.text = "triangle was clicked";
}

On the next page, we'll get even more brutal with it. Things are going to get a bit worse, but then later they'll begin to get better. Just like real life! Yay!

Even more brute force programming

Next, we'll add more event listeners for our objects. We'll add event listeners for MouseEvent.ROLL_OVER and MouseEvent.ROLL_OUT. Once again, carrying on with our brute force technique, we'll write separate functions for each object, and tailor each action inside each function to the specific object. Here's how the swf behaves now:

Here's the brutal code that makes all this magic happen:

import flash.events.MouseEvent;

square.buttonMode = true;
circle.buttonMode = true;
triangle.buttonMode = true;

square.addEventListener(MouseEvent.CLICK, square_onClick);
circle.addEventListener(MouseEvent.CLICK, circle_onClick);
triangle.addEventListener(MouseEvent.CLICK, triangle_onClick);

function square_onClick(event:MouseEvent):void {
	myText.text = "square was clicked";
}
function circle_onClick(event:MouseEvent):void {
	myText.text = "circle was clicked";
}
function triangle_onClick(event:MouseEvent):void {
	myText.text = "triangle was clicked";
}

square.addEventListener(MouseEvent.ROLL_OVER, square_onOver);
circle.addEventListener(MouseEvent.ROLL_OVER, circle_onOver);
triangle.addEventListener(MouseEvent.ROLL_OVER, triangle_onOver);

function square_onOver(event:MouseEvent):void {
	square.alpha = 0.5;
}
function circle_onOver(event:MouseEvent):void {
	circle.alpha = 0.5;
}
function triangle_onOver(event:MouseEvent):void {
	triangle.alpha = 0.5;
}

square.addEventListener(MouseEvent.ROLL_OUT, square_onOut);
circle.addEventListener(MouseEvent.ROLL_OUT, circle_onOut);
triangle.addEventListener(MouseEvent.ROLL_OUT, triangle_onOut);

function square_onOut(event:MouseEvent):void {
	square.alpha = 1;
}
function circle_onOut(event:MouseEvent):void {
	circle.alpha = 1;
}
function triangle_onOut(event:MouseEvent):void {
	triangle.alpha = 1;
}

It's very common to add event listeners to objects for OVER, OUT, and CLICK, you'll find yourself doing it all the time. Adding more objects to the party means writing those same three listeners again for each one you add (and the addEventListener line as well)! Unless one is a huge fan of cut, copy and paste (and edit!), at this point, anyone is bound to be crying out, "There must be a better way! All those functions are doing exactly the same thing, the only thing that varies is the object that they act upon!" Next, we'll see the first streamlining technique, combining listeners.

Combining event listeners

Now, as promised, we're going to see things get a bit better (I told you this was a simple tutorial)!

First, let's drop back a bit, and just deal with the original file, when we were only dealing with the CLICK event alone:

import flash.events.MouseEvent;

square.buttonMode = true;
circle.buttonMode = true;
triangle.buttonMode = true;

square.addEventListener(MouseEvent.CLICK, square_onClick);
circle.addEventListener(MouseEvent.CLICK, circle_onClick);
triangle.addEventListener(MouseEvent.CLICK, triangle_onClick);

function square_onClick(event:MouseEvent):void {
	myText.text = "square was clicked";
}
function circle_onClick(event:MouseEvent):void {
	myText.text = "circle was clicked";
}
function triangle_onClick(event:MouseEvent):void {
	myText.text = "triangle was clicked";
}

You probably know that our event handler function that we write must accept a parameter, which is the event object that it is being passed. It's a rule. It's required to supply this parameter, but it's optional whether we actually use it or not in the body of the function. However, we find that if we do decide to use it, there are a few properties of this event object that we can take advantage of.

Probably one of the most useful ones is the currentTarget property (in this tutorial, I'll ignore the target property. Until you learn the difference though, you should use currentTarget exclusively. See my tutorial on the event flow for a fuller treatment of the difference. In any case, if you use currentTarget you are assured that the object you are addressing is the same one you added the event listener to). This property holds the identity of the object that triggered the event. You might be inclined at first to hang onto instance names. For example, you can understand if the square was clicked, that you should program the square what to do. What I am telling you is that if the square is clicked, the following two things in our event handler will be equal:

  1. square
  2. event.currentTarget

That is, both of the above are really just pointers that point to the object as it sits in memory, and both are just as valid as names. Let's prove it. Each of our objects on the stage has a name property. The fact that we created them on the stage means that Flash does us a little favor and fills in their "name" property for us (if you had created your objects dynamically, then you would have had to manually assign this name property yourself, but I digress). Anyway, let's change line 12 of the above code to this instead:

myText.text = square.name + " was clicked";

Press CTRL-Enter to test the movie. Now we are grabbing the name property from the "square" object, instead of supplying this text ourselves. Next, change line 12 to this instead and run it:

myText.text = event.currentTarget.name + " was clicked";

You will find that it behaves exactly the same way as before. And obviously, we could go through and change this in the other two event handlers as well. But now that we know that event.currentTarget can point to whichever object got the event, we see that we could actually combine the three listeners into one, and event.currentTarget is a way of saying "whichever object you are." Really cool! So let's change the code so that all three objects are sharing the same event listener function:

import flash.events.MouseEvent;

square.buttonMode = true;
circle.buttonMode = true;
triangle.buttonMode = true;

square.addEventListener(MouseEvent.CLICK, item_onClick);
circle.addEventListener(MouseEvent.CLICK, item_onClick);
triangle.addEventListener(MouseEvent.CLICK, item_onClick);

function item_onClick(event:MouseEvent):void {
	myText.text = event.currentTarget.name + " was clicked";
}

Notice that now the one listener is named item_onClick, and this name reflects the fact that it is now a more generic function that can handle all three objects (not to mention however many more we choose to add later)!

Here is how the swf now behaves, which is exactly how our original example behaved, only now the code that makes it happen is much shorter. Next, we'll see how to use arrays and loops to streamline the other part of the code, where we set the buttonMode property and add all the event listeners!

More streamlining using arrays and loops

Next, we'll put our three objects into an array, and then later process them as a group using a loop.

First, what is an array? Basically, an array is a data structure that allows you to group items. But when you put items into an array, they don't actually move there. Rather, the array just gives you a new way to reference the exact same items. But probably the best way to tell you how this works is just to show you. So let's create an array variable to group our three items, like so:

var clipArray:Array = [square, circle, triangle];

(I have gone over this before in my tutorial on "Buttons in an Array," so forgive me for being redundant and saying it all again, but this is in a different context anyway.)

Now that I have declared the array, and grouped the items in it, it's as though I now have three brand new instance names to use (albeit using brackets and index numbers):

Therefore, the following two lines would accomplish exactly the same thing:

  1. square.buttonMode = true;
  2. clipArray[0].buttonMode = true;

Similarly, these next two lines would accomplish the same thing:

  1. square.addEventListener(MouseEvent.CLICK, item_onClick);
  2. clipArray[0].addEventListener(MouseEvent.CLICK, item_onClick);

I'm driving home the point that using the array reference is just as good as using the instance name. They both point to the same object!

Now, let's take it a bit further. The index number in the brackets might also be represented by a variable. Let's suppose that we set a variable called "i" (for index), make it an integer type, and give it a value, like so:

var i:int = 0;

Having created this variable and setting it equal to 0, now we can do the following:

clipArray[i].buttonMode = true;
clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);

With i being equal to 0, the above operations are once again being carried out on the square. If we set i equal to 1, then we are affecting the circle. And if we set i equal to 2, then we are affecting the triangle. In fact, all we have to do to affect all the objects in the array is to have a mechanism for changing the value of i repeatedly (consider the following code snippet):

var i:int = 0;
clipArray[i].buttonMode = true;
clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
i = 1;
clipArray[i].buttonMode = true;
clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
i = 2;
clipArray[i].buttonMode = true;
clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);

Notice that those two lines of code that keep getting repeated don't change at all, the only thing that changes is the value of "i" just before the lines execute. This causes the same two lines to affect three different objects, each in turn, just by changing the value of i. However, in code, whenever you have identical lines that get repeated, it usually means there is a way to combine it somehow. This is what for loops accomplish nicely.

Loops are sections of code that run a certain number of times, in fact, however many times we specify. The for loop's set of parentheses contains three parts, separated by semi-colons. In the first part, a variable is initialized (and optionally declared also). This variable will serve as a loop counter, and it's kind of conventional to use the letter "i" (meaning iteration, or index). You can name this variable whatever you want, but we'll stick with "i" for our purposes. In the second part, a condition is set using a Boolean expression. As long as this condition is true, the loop will keep going. Finally, the third part is a statement of how much to increment the variable each time.

While the for loop's counter variable can be initialized to whatever you want, it's conventional to initialize it to 0. This makes it easy to combine the loop with an array, since arrays are 0 based. Let's write a for loop that initializes i to 0, loops three times, and increments i by 1 each time:

for(var i:int = 0; i < 3; i++) {
    trace(i);
}

The above three lines of code will produce this output:

0
1
2

Let's examine what is going on with this simple loop:

So you can see that if you initialize a loop with 0, then you make the condition to be "less than" the number of times you actually want to loop, the loop will execute exactly that number of times. 

Now, let's apply this loop structure to our array:

for(var i:int = 0; i < 3; i++) {
    clipArray[i].buttonMode = true;
    clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
}

Can you see the substitutions that are taking place here? The first time through the loop, when i = 0, the code will act upon the square. The second time through the loop, when i = 1, the same two lines will affect the circle. The third time through the loop, when i = 2, the same two lines will affect the triangle. Finally, when i = 3, the loop will terminate without attempting to run those lines on clipArray[3], which is a good thing, because there isn't any such element, and that would cause an error.

Now, the only other thing that the above lacks is that it uses the hard-coded number 3. Instead, we will use the length property that the array class provides. Every array "knows" its own length, whatever it is, and the length of the clipArray as it stands right now is 3. Although the array's length is 3, the highest index is 2. This is just how it works. The three elements have indices of 0, 1, and 2. But the length is 3. I'm just emphasizing this so you won't ever confuse the length with the highest index. The highest index of an array will always be its length minus one. Anyway, here's our new loop rewritten to use the length property instead of the hard coded number 3:

for(var i:int = 0; i < clipArray.length; i++) {
    clipArray[i].buttonMode = true;
    clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
}

The beauty of this system is that, later, if we add another element to the clipArrray, this loop need not be edited again, it will work exactly the same way. Suppose we added another MovieClip to the array with an instance name of star (after we create it on the stage and give it that instance name, of course). We would just add star to the array. Now the array's length would be 4, so the same loop will run four iterations instead of just three, with no changes at all to the looping code! And the star would all of a sudden be included in all that processing that's happening to the other clips!

Here's the code that combines event listeners and uses arrays and loops, and even with whitespace, now it's only 12 lines:

import flash.events.MouseEvent;

var clipArray:Array = [square, circle, triangle];

for(var i:int = 0; i < clipArray.length; i++) {
	clipArray[i].buttonMode = true;
	clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
}

function item_onClick(event:MouseEvent):void {
	myText.text = event.currentTarget.name + " was clicked";
}

Finally, here's the code that will set buttonMode to true and add event listeners for ROLL_OVER, ROLL_OUT, and CLICK, and do it all with shared listeners, arrays and loops!

import flash.events.MouseEvent;

var clipArray:Array = [square, circle, triangle];

for(var i:int = 0; i < clipArray.length; i++) {
	clipArray[i].buttonMode = true;
	clipArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
	clipArray[i].addEventListener(MouseEvent.ROLL_OVER, item_onOver);
	clipArray[i].addEventListener(MouseEvent.ROLL_OUT, item_onOut);
}

function item_onClick(event:MouseEvent):void {
	myText.text = event.currentTarget.name + " was clicked";
}
function item_onOver(event:MouseEvent):void {
	event.currentTarget.alpha = 0.5;
}
function item_onOut(event:MouseEvent):void {
	event.currentTarget.alpha = 1;
}

Here's how the swf behaves now. It behaves identically to the brute force version. However, it's code is now 20 lines instead of 47. And that's only with 3 objects involved!

This tutorial is more basic than much of my other ones, but I also know that a lot of people out there possibly need this. I also used just a simple example; your actual code or project might be much more complex and involved than this, but the principles are still the same. I myself have used these techniques to shorten other peoples' code, many times by as much as 75 to 80%, so I've seen 500 lines become 100, 1200 lines become 200, etc. And yet the shorter code does exactly the same thing as the longer! Also, once you understand how it works, the shorter code is easier to read, find your way around in, and especially to maintain!