ActionScript drop-down menu – part 2 of 3
This is the second part of the lesson on building a drop down menu with ActionScript.
2. ActionScript code that powers the drop-down menu
2.1 Name the first layer on the main scene actions and lock it. Then click on its first frame to select it.
2.2 Open the Actions panel by selecting Window > Actions.
2.3 Insert the following ActionScript code inside the panel:
var menuButtons:Array = ["Home", "Works", "About", "Contact"];
var subMenu1:Array = ["News", "Updates"];
var subMenu2:Array = ["Web", "Print", "Interactive", "Audio", "Video"];
var subMenu3:Array = ["About me", "Services", "Resume"];
var subMenu4:Array = ["Email me", "City map"];
var chosenMenu:Array = new Array();
var subMenuOpened:Boolean = false;
var whichSubMenu:Number = new Number();
var currentPosition:Number = new Number();
for (i=0; i
this["menuButton"+i]._x = 30+(115*i);
this["menuButton"+i]._y = 6;
this["menuButton"+i].label_txt.text = menuButtons[i];
this["menuButton"+i].onRollOver = function():Void {
whichSubMenu = Number(this._name.substr(-1, 1));
currentPosition = this._x;
if (!subMenuOpened) {
subMenuOpened = true;
openSubMenu(whichSubMenu, currentPosition);
} else {
closeSubMenu();
openSubMenu(whichSubMenu, currentPosition);
}
};
}
function openSubMenu(whichSubMenu, currentPosition):Void {
chosenMenu = eval("subMenu"+(whichSubMenu+1));
for (j=0; j
this.createEmptyMovieClip("subMenuHolder_mc", this.getNextHighestDepth());
subMenuHolder_mc.attachMovie("menu button", "subMenuButton"+j, this.getNextHighestDepth());
subMenuHolder_mc["subMenuButton"+j]._x = currentPosition;
subMenuHolder_mc["subMenuButton"+j]._y = 29+(j*23);
subMenuHolder_mc["subMenuButton"+j].label_txt.text = chosenMenu[j];
subMenuHolder_mc["subMenuButton"+j].onRelease = subMenuHolder_mc["subMenuButton"+j].onReleaseOutside=function ():Void {
closeSubMenu();
};
}
}
function closeSubMenu():Void {
removeMovieClip(subMenuHolder_mc);
}
2.4 Test your SWF movie now by selecting Control > Test Movie.
You will see the basic drop-down menu appear, with all the buttons in the same color (the one that you chose for the button’s background earlier in the tutorial). Try moving your mouse over the main menu buttons (the upper row) and you’ll see the drop-down menus appear and disappear. Also, you will see that a menu will close if you click on any of its buttons.
Let me explain you now how this works and later you’ll see how to change the color of your buttons.
2.5 The variables. This is the easiest part to understand — initializing the variables in ActionScript.
var menuButtons:Array = ["Home", "Works", "About", "Contact"];
var subMenu1:Array = ["News", "Updates"];
var subMenu2:Array = ["Web", "Print", "Interactive", "Audio", "Video"];
var subMenu3:Array = ["About me", "Services", "Resume"];
var subMenu4:Array = ["Email me", "City map"];
var chosenMenu:Array = new Array();
var subMenuOpened:Boolean = false;
var whichSubMenu:Number = new Number();
var currentPosition:Number = new Number();
As you can see from the code snippet above, there are six arrays created. They serve to store the following information:
- the
menuButtons
array stores the main menu links, which appear immediately after your SWF has been loaded. - the
subMenu1
throughsubMenu4
arrays store the links (the link names, that is) for each submenu.subMenu1
stores the sublinks for the “Home” menu, and so on. - The
chosenMenu
array will serve to temporarily store the currently displayed menu, depending over which one the user rolled her mouse over.
Then, there is the subMenuOpened
variable which is of the Boolean type — the only acceptable values for this type of variable are either true or false. As its name makes clear, this variable will be used to tell Flash whether a drop-down menu is opened or not. Its initial value is set to false, of course, because when your SWF loads up, there is no menu opened until the user interacts with them.
The whichSubMenu
variable is a number which will tell Flash what drop-down menu has been accessed, so that knows which one it must close when a user clicks a link or opens another drop-down menu.
And lastly, the currentPosition
variable is of the Number type too, and it will serve to tell Flash how to position an opened drop-down menu. You must specifiy this so that the right submenu opens under the proper button and not at some random position.
2.6 Now comes the main for
loop whose purpose is manifold:
- It will pull out the buttons from the Library dynamically and place them horizontally, creating the main menu.
- Each button will have its unique label (link text) assigned.
- All of the buttons will have their onRollOver event handlers defined, so that their drop-down menus will open when the user rolls his mouse over them.
for (i=0; i
this["menuButton"+i]._x = 30+(115*i);
this["menuButton"+i]._y = 6;
this["menuButton"+i].label_txt.text = menuButtons[i];
this["menuButton"+i].onRollOver = function():Void {
whichSubMenu = Number(this._name.substr(-1, 1));
currentPosition = this._x;
if (!subMenuOpened) {
subMenuOpened = true;
openSubMenu(whichSubMenu, currentPosition);
} else {
closeSubMenu();
openSubMenu(whichSubMenu, currentPosition);
}
};
}
Let’s see this loop more closely.
The loop is defined so that it starts at zero (i=0
) and it exists while i
stays lesser than the length of the menuButtons
array (the length of an array is the number of elements inside it): i < menuButtons.length
. As you saw a little bit earlier, the menuButtons
array has four elements inside it:
var menuButtons:Array = ["Home", "Works", "About", "Contact"];
These elements are the main menu buttons/links. So it makes sense that the loop must not surpass their number — you need to define a drop-down menu for each of these buttons/links, and that’s it.
The first line of ActionScript inside the loop is the one which dynamically attaches the movie clip from the Library onto the stage. This is the menu button symbol that you have created earlier.
this.attachMovie("menu button", "menuButton"+i, this.getNextHighestDepth());
The attachMovie()
command has 3 parameters that must be passed onto it:
- The Identifier. This is the identifier name that you gave to the movie clip in the Library. Flash will read it and find it in the Library. You must write it exactly the same as you set it up in the Library.
- The new Instance name of the newly attached movie clip. This is a must, otherwise you wouldn’t be able to control it later.
- The depth level. This is the stacking order of the movie clip, in relation to the timeline and any other existing objects on the stage. This is like an invisible layer onto which the movie clip has been placed.
The Instance name for each new menu button is created dynamically: it will be menuButton0 at the first loop iteration, menuButton1 at the second, menuButton2 at the third and menuButton3 at the fourth. Each menu button must have its own unique name.
Also, each depth level must be unique too, because no two movie clips can exist at the same depth stacking level. So by using the getNextHighestDepth()
command you tell Flash to assign the new movie clip the first free depth level that it finds. This is a fine method because you don’t have to keep track of depth levels yourself.
Lastly, the ActionScript keyword this
points to the timeline or object it is situated on. Since all of the code here is being placed on the main timeline, this points to the main or root timeline — the SWF movie itself.
Now that the main menu button has been attached, you must position it and give it a label:
this["menuButton"+i]._x = 30+(115*i);
this["menuButton"+i]._y = 6;
this["menuButton"+i].label_txt.text = menuButtons[i];
The construct this["menuButton"+i]
is pointing to the newly attached movie clip. This has to be a dynamic construct because the movie clip Instance name changes with each iteration of the loop. So each time you tell Flash “Listen, I want you to move this newly made movie clip here and…” etc.
First, the button’s horizontal position is determined through the movie clip’s _x
property:
this["menuButton"+i]._x = 30+(115*i);
This property has a pixel value. The number 30 is the spacing from the left edge of the stage. I just chose 30 like that — it seemed fine to me. You can change it into any dimension you deem appropriate. Inside the parenthesis is the number 115 — 115 pixels being a little bit more than the button’s width. This will make for nice horizontal positioning and spacing between the main menu buttons. And finally, the variable i
which increases by 1 with each iteration of the loop makes sure that the buttons will be spaced evenly and that they won’t overlap. Here’s a nice visual representation of this process and the result:
The next line of code serves to determine the vertical position of the menu buttons, through the _y
property.
this["menuButton"+i]._y = 6;
They are all placed at the same vertical position, of course, so that you have a nicely aligned website menu and also because all the drop-down menus must be placed properly, at the same height, afterwards.
And now, you must define the onRollOver event handler for each of these buttons, so that the drop-down menus actually show up when the user rolls his mouse over the main menu. Here it is:
this["menuButton"+i].onRollOver = function():Void {
whichSubMenu = Number(this._name.substr(-1, 1));
currentPosition = this._x;
if (!subMenuOpened) {
subMenuOpened = true;
openSubMenu(whichSubMenu, currentPosition);
} else {
closeSubMenu();
openSubMenu(whichSubMenu, currentPosition);
}
};
The first line tells Flash that you are going to create an onRollOver event handler function. The function’s contents are between curly braces: {
and }
. So when the user rolls the mouse over a menu button, this code will be executed.
The first line serves to extract the number from the current menu button’s Instance name and to store it inside the whichSubMenu
variable:
whichSubMenu = Number(this._name.substr(-1, 1));
This is done via the substr()
command which extracts a piece of text from an existing text. So, the construct this._name.substr
means “extract a piece of text from this name”. The name is the Instance name of the current button (this._name
).
And the text that is to be extracted is the last character from the Instance name. How come? Well, that’s easy: suppose that the loop is on its third iteration, meaning that the Instance name of the current movie clip is menuButton2. The first parameter of the substr()
command is the point where the extraction of text starts. When it’s -1, this means that the extraction begins at the last character — at the end of the word. And the second parameter is the number of characters to be extracted. So, in this case, the Instance name is menuButton2 and the character that is extracted is “2”.
Then, this extracted character is passed through the Number
function. That’s because you will need a number to be able to tell Flash which drop-down menu to open and the object that you have extracted is a string, meaning a piece of text. You have to convert this piece of text into a numerical (mathematical) value, otherwise Flash will consider it as a simple character and nothing more.
Fine. Once this is done, Flash also needs to know the horizontal position (coordinate) of the main menu button that the user has interacted with (rolled his mouse over it). This gets stored in the currentPosition
variable for later usage:
currentPosition = this._x;
What follows is an if/else
conditional statement…
if (!subMenuOpened) {
subMenuOpened = true;
openSubMenu(whichSubMenu, currentPosition);
} else {
closeSubMenu();
openSubMenu(whichSubMenu, currentPosition);
}
…which, translated into English, says:
If the variable subMenuOpened
turns out as false
,
change its value to true
and
open the drop-down menu (using the values of the variables whichSubMenu
and currentPosition
).
On the other hand, if subMenuOpened
equals true
,
Close any drop-down menu that might be open and
open the appropriate (current button’s) drop-down menu.
The value of subMenuOpened
equals false
when your SWF loads at first, because you had set it that way in the first part of your ActionScript code. So the openSubMenu()
function will be executed, with the values of whichSubMenu
and currentPosition
variables passed to it. Let me explain you this function now — it opens a drop-down menu.
2.7 Here is the code of the openSubMenu()
function:
function openSubMenu(whichSubMenu, currentPosition):Void {
chosenMenu = eval("subMenu"+(whichSubMenu+1));
for (j=0; j
this.createEmptyMovieClip("subMenuHolder_mc", this.getNextHighestDepth());
subMenuHolder_mc.attachMovie("menu button", "subMenuButton"+j, this.getNextHighestDepth());
subMenuHolder_mc["subMenuButton"+j]._x = currentPosition;
subMenuHolder_mc["subMenuButton"+j]._y = 29+(j*23);
subMenuHolder_mc["subMenuButton"+j].label_txt.text = chosenMenu[j];
subMenuHolder_mc["subMenuButton"+j].onRelease = subMenuHolder_mc["subMenuButton"+j].onReleaseOutside=function ():Void {
closeSubMenu();
};
}
}
The first thing that is being created inside the function is the reference to the current subMenu — the appropriate drop-down menu that has to be opened, the one below the selected button.
chosenMenu = eval("subMenu"+(whichSubMenu+1));
This is done via the eval
method (command, function) which takes a string (a piece of text) and returns the reference to an Object (a movie clip, button, etc).
To continue on the same example I gave before, let’s say that the loop was on the third iteration. This means that whichSubMenu
equals 2. The evaluation would go like this:
chosenMenu = eval("subMenu"+(whichSubMenu+1));
chosenMenu = eval("subMenu"+(2+1));
chosenMenu = eval("subMenu"+3);
chosenMenu = eval("subMenu3");
chosenMenu = subMenu3;
You certainly noticed that the value of whichSubMenu
had 1 added to it. That’s because I have named the drop-down menus subMenu1
, subMenu2
, subMenu3
and subMenu4
. And since the main loop starts with i
set to 0, you have to add 1 to this value to be able to match each submenu to its main menu button.
Fine! So in my example, subMenu3
was chosen by the user. Just as a little reminder before moving on to the next line of ActionScript, here’s the subMenu3
array (it was initialized at the beginning of your code):
var subMenu3:Array = ["About me", "Services", "Resume"];
OK, now comes the second loop, which actually makes the drop-down menu appear:
for (j=0; j
subMenuHolder_mc.attachMovie("menu button", "subMenuButton"+j, this.getNextHighestDepth());
subMenuHolder_mc["subMenuButton"+j]._x = currentPosition;
subMenuHolder_mc["subMenuButton"+j]._y = 29+(j*23);
subMenuHolder_mc["subMenuButton"+j].label_txt.text = chosenMenu[j];
subMenuHolder_mc["subMenuButton"+j].onRelease = subMenuHolder_mc["subMenuButton"+j].onReleaseOutside=function ():Void {
closeSubMenu();
};
}
I chose to use j
as the variable for this loop and, like the last one, this one starts with the value of 0 (zero) too. The condition for the loop to exist is that j
must be lesser than the length of the chosenMenu
array. Again, this makes perfect sense because you need to create the drop-down submenu with the exact number of elements that are inside the chosenMenu
array.
The first thing that has to be done is the creation of a placeholder movie clip. This is an excellent choice, because you will be able to remove the whole drop-down menu more easily later by just removing this movie clip and not having to loop again to erase all the buttons, because all the buttons are going to be nested inside this empty placeholder.
this.createEmptyMovieClip("subMenuHolder_mc", this.getNextHighestDepth());
The easiest and quickest way to create a new movie clip instance is via the createEmptyMovieClip()
method. Again, the ActionScript keyword “this
” means that this empty movie clip will be created on the main timeline, because the above line of code (and all the other code, actually) is on the main (root) timeline.
The first parameter of this method is the Instance name of the new movie clip and the second one assigns it a new depth level (like an invisible layer, remember), in the same way as it was done with the attachMovie()
method.
Speaking of which, you will use it right now to attach the buttons to the drop-down menu in question:
subMenuHolder_mc.attachMovie("menu button", "subMenuButton"+j, this.getNextHighestDepth());
The empty movie clip placeholder (subMenuHolder_mc
) just had the menu button movie clip from the Library attached to it.
You have to properly position the newly attached menu button now. The main difference from the positioning of the main menu buttons and these ones is that here all the buttons have the same horizontal position (_x
equals currentPosition
, which is the position of the main menu button which has been chosen by the user), while their vertical position is different, because you have to stack them one below the other.
subMenuHolder_mc["subMenuButton"+j]._x = currentPosition;
subMenuHolder_mc["subMenuButton"+j]._y = 29+(j*23);
The vertical position of each of the menu buttons is calculated in a similar way as was done for the main menu buttons. The base distance from the chosen main menu button/link (29 pixels) has the product of j
multiplied by 23 added to it. 23 pixels seemed just fine as the space between the submenu buttons.
After that, you have to make Flash write the link label inside the button (in the dynamic text field inside it, which you created before — label_txt
, remember).
subMenuHolder_mc["subMenuButton"+j].label_txt.text = chosenMenu[j];
The link text which will appear for the current drop-down menu button is determined by way of finding it inside the appropriate array. The array is chosenMenu
and inside it, the position of the element that has to be read and inserted in this button’s text field is j
(the current iteration of the loop), which is a number. Keep in mind that the first element inside an Array in ActionScript has the position 0 (zero).
After that, the onRelease
event handler for the current drop-down menu button has to be defined. As you can see below, I have defined the same function for both the onRelease
and onReleaseOutside
event handlers. Why did I chose to do it like that? Well, the onRelease event is a normal, standard click. But if a user clicks on a submenu button, hold his mouse button, moves the mouse away from the button and releases it — that won’t be counted as an onRelease event, but rather as onReleaseOutside. So by defining the same function for both events, you are making sure to cover both situations.
subMenuHolder_mc["subMenuButton"+j].onRelease = subMenuHolder_mc["subMenuButton"+j].onReleaseOutside=function ():Void {
closeSubMenu();
};
Inside the function, the only thing that will be executed is a single line of code that calls the closeSubMenu()
function. This function will obviously close the menu. This is logical: once a user has made a choice and clicked a button, the menu must close. How to use this to navigate a Flash website, I will explain later. Let me show you now how the drop-down menu gets closed.
2.8 The function for closing down an opened drop-down menu is really simple. The only thing it does is it removes the previously created subMenuHolder_mc
empty movie clip, which contains all the drop-down menu buttons.
function closeSubMenu():Void {
removeMovieClip(subMenuHolder_mc);
}
Whew! That was a lengthy explanation, wasn’t it? But I want you to learn and understand how this functions :-).
Proceed to the last section to see how the drop-down menus are going to be colored.