Sunday, August 29, 2010

YUI "website top nav" Menu from JavaScript only

I recently had the opportunity to observe someone running into some difficulties trying to implement a YUI 2 Menu. The menu items were to be retrieved from a database, and needed to be dynamically updatable through an AJAX call - so using JSON rather than HTML to build the menu made sense in this case. According to YUI's menu page, "Menus can be created from simple, semantic markup on the page or purely through JavaScript".

In this case, getting the menu built "purely through JavaScript" was looking to be a little bit of a challenge. The "Website Top Nav With Submenus" was the type of menu desired. YUI's available menu examples included this type of menu built both "From Markup" and "From JavaScript". However, the "From JavaScript" version still built the top-level menu with HTML rather than JavaScript. While certainly possible, dynamically generating both HTML and JavaScript from the server-side certainly didn't seem ideal.

Recognizing that YUI is a very robust JavaScript library, I knew that there had to be a way to build the menu with only JavaScript. After starting with a new test script, I quickly found the 2 main parts to the solution:

  1. The MenuBar constructor accepts an "itemData" property as part of its 2nd "config" argument. The menu structure can be configured here for the top level, in a fashion identical to the "aSubmenuData" array of objects visible in the original YUI example.
  2. All menu items - including the top-level menus, submenus, and the menu bar itself - must have a unique ID for the HTML DOM, otherwise the item will not activate. For example, if a top-level menu is missing an ID, it will display, but not activate to display the submenus.

    Ideally, it'd be nice to have the "id" property optional. However, the "YAHOO.util.Dom.generateId()" function works well for this - and at least for the purposes of this example, I aliased it as simply "id".

The complete updated example is shown below. For lack of better data, the same example data from the original YUI example is used:

JavaScript source code:

  YAHOO.util.Event.onDOMReady(function(){
    
    var id = YAHOO.util.Dom.generateId;
    
    var menuData = [
      {
        text: "Communication",
        submenu: {id: id(), itemdata: [ 
          {text: "360", url: "http://360.yahoo.com"},
          {text: "Alerts", url: "http://alerts.yahoo.com"},
          {text: "Avatars", url: "http://avatars.yahoo.com"},
          {text: "Groups", url: "http://groups.yahoo.com"},
          {text: "Internet Access", url: "http://promo.yahoo.com/broadband"},
          {
            text: "PIM", 
            submenu: { 
              id: id(), 
              itemdata: [
                {text: "Yahoo! Mail", url: "http://mail.yahoo.com"},
                {text: "Yahoo! Address Book", url: "http://addressbook.yahoo.com"},
                {text: "Yahoo! Calendar",  url: "http://calendar.yahoo.com"},
                {text: "Yahoo! Notepad", url: "http://notepad.yahoo.com"}
              ] 
            }
          }, 
          {text: "Member Directory", url: "http://members.yahoo.com"},
          {text: "Messenger", url: "http://messenger.yahoo.com"},
          {text: "Mobile", url: "http://mobile.yahoo.com"},
          {text: "Flickr Photo Sharing", url: "http://www.flickr.com"},
        ]}
      },
      {
        text: "Shopping",
        submenu: {id: id(), itemdata: [
          {text: "Auctions", url: "http://auctions.shopping.yahoo.com"},
          {text: "Autos", url: "http://autos.yahoo.com"},
          {text: "Classifieds", url: "http://classifieds.yahoo.com"},
          {text: "Flowers & Gifts", url: "http://shopping.yahoo.com/b:Flowers%20%26%20Gifts:20146735"},
          {text: "Real Estate", url: "http://realestate.yahoo.com"},
          {text: "Travel", url: "http://travel.yahoo.com"},
          {text: "Wallet", url: "http://wallet.yahoo.com"},
          {text: "Yellow Pages", url: "http://yp.yahoo.com"}
        ]}
      },
      {
        text: "Entertainment",
        submenu: {id: id(), itemdata: [
          {text: "Fantasy Sports", url: "http://fantasysports.yahoo.com"},
          {text: "Games", url: "http://games.yahoo.com"},
          {text: "Kids", url: "http://www.yahooligans.com"},
          {text: "Music", url: "http://music.yahoo.com"},
          {text: "Movies", url: "http://movies.yahoo.com"},
          {text: "Radio", url: "http://music.yahoo.com/launchcast"},
          {text: "Travel", url: "http://travel.yahoo.com"},
          {text: "TV", url: "http://tv.yahoo.com"}
        ]}
      },
      {
        text: "Information",
        submenu: {id: id(), itemdata: [
          {text: "Downloads", url: "http://downloads.yahoo.com"},
          {text: "Finance", url: "http://finance.yahoo.com"},
          {text: "Health", url: "http://health.yahoo.com"},
          {text: "Local", url: "http://local.yahoo.com"},
          {text: "Maps & Directions", url: "http://maps.yahoo.com"},
          {text: "My Yahoo!", url: "http://my.yahoo.com"},
          {text: "News", url: "http://news.yahoo.com"},
          {text: "Search", url: "http://search.yahoo.com"},
          {text: "Small Business", url: "http://smallbusiness.yahoo.com"},
          {text: "Weather", url: "http://weather.yahoo.com"}
        ]}
      }
    ];
    
    new YAHOO.util.YUILoader({require: ["menu"], onSuccess: function(){
      var oMenuBar = new YAHOO.widget.MenuBar(id(), {
        autosubmenudisplay: true, 
        hidedelay: 750, 
        itemdata: menuData});
      
      oMenuBar.render("com.ziesemer.demos.yuiMenuJsOnly.global_menu_parent");
    }}).insert();
  });

HTML source code:

  <div class="yui-skin-sam">
    <div id="com.ziesemer.demos.yuiMenuJsOnly.global_menu_parent" class="yuimenubarnav"></div>
  </div>

Output:

No comments: