XML-Driven Drop-Down Menu
         by senocular

Generating a Menu
The GenerateMenu function follows these steps:

  1. Set up some variables to be used in the function call.

     
    var curr_node;
    var curr_item;
    var curr_menu ...

     

  2. Create an empty movieclip for the menu to live in within the passed container argument with the name given by the name argument at the depth given by the depth argument. Assign this clip to one of the variables created for future reference.

     
    var curr_menu = container.createEmptyMovieClip(name, depth);

     

  3. For each node in the passed node_xml, create a menu item movieclip by attaching an instance of menuitem from the library.

     
    for (var i=0; i<node_xml.childNodes.length; i++) {

     

  4. Position each menu item correctly so each following item is placed below the previous and set each to trackAsMenu (this allows for dragOver events to work as well as rollOver events).

     
    curr_item._x = x;
    curr_item._y = y + i*curr_item._height;
    curr_item.trackAsMenu = true;

     

  5. Save the attributes of each XML node (name, action and variables) to the item created for that node so that they may be referenced when needed, the name value being placed in the textfield in the attached menu item clip.

     
    curr_node = node_xml.childNodes[i];
    curr_item.action = curr_node.attributes.action;
    curr_item.variables = curr_node.attributes.variables;
    curr_item.name.text = curr_node.attributes.name;

     

  6. Assign rollOver actions to each node, opening a submenu if a menu node and closing a submenu if an item node. When opening a menu, re-use GenerateMenu and position the new menu to the left of the current menu. The node for this menu is the current node which, when GenerateMenu is called for it, will make the new submenu based on all the nodes within this one. In closing a menu, any submenu_mc in the current menu is simply removed.

     
    if (node_xml.childNodes[i].nodeName == "menu"){
    curr_item.node_xml = curr_node;
    curr_item.onRollOver = curr_item.onDragOver = function(){
    var x = this._x + this._width - 5;
    var y = this._y + 5;
    GenerateMenu(curr_menu, "submenu_mc", x, y, 1000, this.node_xml);
    };
    }else{ // node is an item node
    curr_item.arrow._visible = false;
    curr_item.onRollOver = curr_item.onDragOver = function(){
    curr_menu.submenu_mc.removeMovieClip();
    };
    }

     
    Notice how here, too, that the arrow movieclip in the attached menuitem is set to have 0 visibility for non-submenu opening menus. The arrow serves as a visual indicator that the item opens a submenu. By having it in the menuitem movieclip, its assumed that all items start as menu-opening items. For those that are not, it is simply hidden.
     

  7. Finally, assign a release action for each item (menu and otherwise) which calls the required action and closes all submenus.

     
    curr_item.onRelease = function(){
    Actions[this.action](this.variables);
    CloseSubmenus();
    };

And... ACTION!
Notice how the actions are executed. You have some sort of Actions object with a reference to the action of the node with the variables passed in to it (as its being called as a function).

Actions[this.action](this.variables);

The Actions object is, in fact, a custom object. This is the object to handle your actions, obviously. It contains functions with names that are similar to that which would be passed in to the action attribute of an XML node. The variables value is then passed in along with that. Consider the gotoURL action from previous examples. You would end up with a call such as.

Actions["gotoURL"]("http://www.google.com");

This calls the gotoURL function in the Actions object passing http://www.google.com along as an argument. You of course would need to make an Actions object and define what gotoURL is before being able to use it. Here's an example that will open the passed url in a new window.

Actions = Object();
Actions.gotoURL = function(urlVar){
getURL(urlVar, "_blank");
};

For any action you wish to be able to use, you would create a function in the Actions object in this manner. Whatever functions you have there can then be used in the XML definition to specify what is to happen when a menu item is pressed.

In the final menu example, a textfield is placed at the bottom of the screen. An action in the Actions object is then used to change that text. The function for that is as follows where message_txt is the name of that textfield.

Actions.message = function(msg){
message_txt.text = msg;
};

New Menu Please
There's one more action that I want to include, and thats an action that will load a new menu XML file into the XML object used to handle the menu.

Actions.newMenu = function(menuxml){
menu_xml.load(menuxml);
};

When loaded, any XML that was within the menu_xml object will be replaced and the onLoad event of that object will be called. Remember what's in the onLoad event? That's right, script used to generate the main menu. Whenever you load a new XML file into that object and re-call that main menu generating script, any existing menu is replaced and the new menu is created in its place. This means a simple load command is all that is needed to completely change your menu. If you didn't already notice, the last menu in the posted example does this (titled load menu).

 

 

 




SUPPORTERS:

kirupa.com's fast and reliable hosting provided by Media Temple.