YUI 3: MenuNav Node Plugin [beta]

The MenuNav Node Plugin makes it easy to transform existing list-based markup into traditional, drop down navigational menus that are both accessible and easy to customize, and only require a small set of dependencies. The MenuNav Node Plugin features:

Progressive Enhancement
The MenuNav Node Plugin is designed to support Progressive Enhancement, making it easy to transform simple, semantic markup into dynamic drop-down menus with just a few lines of JavaScript.
Small footprint
The MenuNav Node Plugin has a small footprint of ~5 KB (GZIP'd) and requires a minimal set of dependencies (YUI, Event, DOM, Node, OOP, ClassNameManager, and the Focus Manager Node Plugin).
Accessibility & Usability Minded
The MenuNav Node Plugin was built with both accessibility and usability in mind. The MenuNav Node Plugin implements established mouse and keyboard interaction patterns to deliver a user experience that is both familiar and easy to use. To that foundation the MenuNav Node plugin adds support for screen readers through the use of the WAI-ARIA Roles and States. Watch this video for a quick a demo of a menu created using the MenuNav Node Plugin using the WAI-ARIA Roles and States running in Firefox 3 using the Window-Eyes screen reader.
Easy to style and configure
The MenuNav Node Plugin is easy to configure. The visual presentation of menus is controlled completely via CSS. A handful of straightforward configuration attributes can be used to make common modifications to a menu's behavior.
<iframe> Shim for IE 6
No menuing system would be complete without an <iframe> to prevent <select> elements from poking through menus in IE 6.

Getting Started

Include Dependencies

The easiest way to include the source files for MenuNav and its dependencies is to add the YUI seed file to your page, using the following script tag, and allow the YUI instance to download any additional files which may be required:

  1. <script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>
<script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>

The YUI instance will automatically pull down MenuNav's source files and any missing dependencies when the node-menunav module is used. This helps you avoid having to manually manage the list of files needed on your page to support multiple components while also optimizing your initial page weight by loading files only when they are required.

If you do want to include file dependencies manually on your page, the YUI Dependency Configurator can be used to determine the list of files you need to include in order to use MenuNav.

The YUI Instance

Once you have the YUI seed file on your page (yui-min.js), you can create a new YUI instance for your application to use and populate it with the modules you need, specified as the first set of arguments to the use method:

  1. // Create new YUI instance, and populate it with the required modules
  2. YUI().use('node-menunav', function(Y) {
  3.  
  4. // MenuNav available, and ready for use.
  5.  
  6. });
// Create new YUI instance, and populate it with the required modules
YUI().use('node-menunav', function(Y) {
 
    // MenuNav available, and ready for use.
 
});

The last argument passed to use is a callback function. The callback function will be invoked as soon as the YUI instance is done downloading any required files missing from your page. Once those files are loaded, your local YUI instance will be supplemented with the classes which make up the node-menunav module and any modules it depends on. A reference to the populated YUI instance (Y) is passed back to your callback function. Within your callback, then, you can start writing your application code based on your own custom instance of YUI.

For more information on creating instances of YUI and the use method, see the YUI Global object documentation.

Next, apply the yui-skin-sam class name to an element that is a parent of the element in which the menu lives. You can usually accomplish this simply by putting the class on the <body> tag:

  1. <body class="yui-skin-sam">
  2.  
<body class="yui-skin-sam">
 

For more information on skinning YUI components and making use of default skins, see our Understanding YUI Skins article.

Basic Setup

To create a menu using the MenuNav Node Plugin, start by including the required markup. The markup for menus created using the MenuNav Node Plugin follows the same pattern established for Widgets, with each menu's content box containing one or more <ul> elements:

Menu Markup Example
  1. <div id="menu-1" class="yui-menu"><!-- Bounding box -->
  2. <div class="yui-menu-content"><!-- Content box -->
  3. <ul>
  4. <!-- Menu items -->
  5. </ul>
  6. </div>
  7. </div>
  8.  
<div id="menu-1" class="yui-menu"><!-- Bounding box -->
	<div class="yui-menu-content"><!-- Content box -->
		<ul>
			<!-- Menu items -->
		</ul>
	</div>
</div>
 

Menuitems also follow the Widget markup pattern, with root node defined using an <li> element:

MenuItem Markup Example
  1. <li class="yui-menuitem"><!-- Bounding box -->
  2. <a class="yui-menuitem-content" href="http://www.yahoo.com"><!-- Content box -->
  3. Yahoo!
  4. </a>
  5. </li>
  6.  
<li class="yui-menuitem"><!-- Bounding box -->
	<a class="yui-menuitem-content" href="http://www.yahoo.com"><!-- Content box -->
	Yahoo!
	</a>
</li>
 

Submenus are defined by wrapping the the menu markup in an <li> element. Each submenu must have have a label. The label should preceed the menu markup, and the label's href attribute should be set to the point to the id of its corresponding submenu.

Submenu Markup Example
  1. <li>
  2. <a class="yui-menu-label" href="#submenu-1">Submenu Label</a><!-- Menu label -->
  3. <div id="submenu-1" class="yui-menu"><!-- Bounding box -->
  4. <div class="yui-menu-content"><!-- Content box -->
  5. <ul>
  6. <!-- Menu items -->
  7. </ul>
  8. </div>
  9. </div>
  10. </li>
  11.  
<li>
	<a class="yui-menu-label" href="#submenu-1">Submenu Label</a><!-- Menu label -->
	<div id="submenu-1" class="yui-menu"><!-- Bounding box -->
		<div class="yui-menu-content"><!-- Content box -->
			<ul>
				<!-- Menu items -->
			</ul>
		</div>
	</div>
</li>
 

Following the patterns illustrated above, the markup for a menu created using the MenuNav Node Plugin comes together as follows:

  1. <div id="menu-1" class="yui-menu"><!-- Root menu bounding box -->
  2. <div class="yui-menu-content"><!-- Root menu content box -->
  3. <ul>
  4. <li class="yui-menuitem"><!-- Menuitem bounding box -->
  5. <a class="yui-menuitem-content" href="http://www.yahoo.com"><!-- Menuitem content box -->
  6. Yahoo!
  7. </a>
  8. </li>
  9. <li>
  10. <a class="yui-menu-label" href="#pim">PIM</a><!-- Submenu label -->
  11. <div id="pim" class="yui-menu"><!-- Submenu bounding box -->
  12. <div class="yui-menu-content"><!-- Submenu content box -->
  13. <ul>
  14. <li class="yui-menuitem">
  15. <a class="yui-menuitem-content" href="http://mail.yahoo.com">Yahoo! Mail</a>
  16. </li>
  17. <li class="yui-menuitem">
  18. <a class="yui-menuitem-content" href="http://addressbook.yahoo.com">Yahoo! Address Book</a>
  19. </li>
  20. <li class="yui-menuitem">
  21. <a class="yui-menuitem-content" href="http://calendar.yahoo.com">Yahoo! Calendar</a>
  22. </li>
  23. <li class="yui-menuitem">
  24. <a class="yui-menuitem-content" href="http://notepad.yahoo.com">Yahoo! Notepad</a>
  25. </li>
  26. </ul>
  27. </div>
  28. </div>
  29. </li>
  30. </ul>
  31. </div>
  32. </div>
  33.  
<div id="menu-1" class="yui-menu"><!-- Root menu bounding box -->
	<div class="yui-menu-content"><!-- Root menu content box -->
		<ul>
			<li class="yui-menuitem"><!-- Menuitem bounding box -->
				<a class="yui-menuitem-content" href="http://www.yahoo.com"><!-- Menuitem content box -->
				Yahoo!
				</a>
			</li>
			<li>
				<a class="yui-menu-label" href="#pim">PIM</a><!-- Submenu label -->
				<div id="pim" class="yui-menu"><!-- Submenu bounding box -->
					<div class="yui-menu-content"><!-- Submenu content box -->
						<ul>
							<li class="yui-menuitem">
								<a class="yui-menuitem-content" href="http://mail.yahoo.com">Yahoo! Mail</a>
							</li>
							<li class="yui-menuitem">
								<a class="yui-menuitem-content" href="http://addressbook.yahoo.com">Yahoo! Address Book</a>
							</li>
							<li class="yui-menuitem">
								<a class="yui-menuitem-content" href="http://calendar.yahoo.com">Yahoo! Calendar</a>
							</li>
							<li class="yui-menuitem">
								<a class="yui-menuitem-content" href="http://notepad.yahoo.com">Yahoo! Notepad</a>
							</li>
						</ul>            
					</div>	
				</div> 
			</li>
		</ul>
	</div>
</div>
 

With the markup for the menu in place, simply get a Node reference for the root menu and then call the plug method, passing in a reference to the MenuNav Node Plugin.

  1. // Call the "use" method, passing in "node-menunav". This will load the
  2. // script and CSS for the MenuNav Node Plugin and all of the required
  3. // dependencies.
  4.  
  5. YUI().use("node-menunav", function(Y) {
  6.  
  7. // Use the "contentready" event to initialize the menu when the subtree of
  8. // element representing the root menu (<div id="menu-1">) is ready to
  9. // be scripted.
  10.  
  11. Y.on("contentready", function () {
  12.  
  13. // The scope of the callback will be a Node instance representing
  14. // the root menu (<div id="menu-1">). Therefore, since "this"
  15. // represents a Node instance, it is possible to just call "this.plug"
  16. // passing in a reference to the MenuNav Node Plugin.
  17.  
  18. this.plug(Y.Plugin.NodeMenuNav);
  19.  
  20. }, "#menu-1");
  21.  
  22. });
  23.  
  24.  
//	Call the "use" method, passing in "node-menunav".  This will load the 
//	script and CSS for the MenuNav Node Plugin and all of the required 
//	dependencies.
 
YUI().use("node-menunav", function(Y) {
 
	//	Use the "contentready" event to initialize the menu when the subtree of 
	//	element representing the root menu (<div id="menu-1">) is ready to 
	//	be scripted.
 
	Y.on("contentready", function () {
 
		//	The scope of the callback will be a Node instance representing 
		//	the root menu (<div id="menu-1">).  Therefore, since "this"
		//	represents a Node instance, it is possible to just call "this.plug"
		//	passing in a reference to the MenuNav Node Plugin.
 
		this.plug(Y.Plugin.NodeMenuNav);
 
	}, "#menu-1");
 
});
 
 

Using the MenuNav Node Plugin

Orientation, Style and Behavior

Configuring Orientation

The root menu of menus built using the MenuNav Node Plugin can have a verical or horizontal orientation. The default orientation for menus is vertical, but can be easily switched to horizontal by applying a class of yui-menu-horizontal to the node representing the root menu's bounding box, as illustrated in the following example:

  1. <div id="menu-1" class="yui-menu yui-menu-horizontal"><!-- Bounding box -->
  2. <div class="yui-menu-content"><!-- Content box -->
  3. <ul>
  4. <!-- Menu items -->
  5. </ul>
  6. </div>
  7. </div>
  8.  
<div id="menu-1" class="yui-menu yui-menu-horizontal"><!-- Bounding box -->
	<div class="yui-menu-content"><!-- Content box -->
		<ul>
			<!-- Menu items -->
		</ul>
	</div>
</div>
 

Once the yui-menu-horizontal class name is applied to the node representing the root menu's bounding box, the menu's keyboard interaction is automatically adjusted for a horizontal orientation. The default "Sam" skin provides the following default visualization for horizontal menus, as illustrated in the follow screen capture of a horizontal menu example:

Screen capture of a horizontal menu rendered with the default Sam Skin
Configuring Style & Behavior

The default "Sam" skin provides two additional visualizations for horizontal menus, each of which can be applied by both changes to the menu label markup and through the application of a class to the node representing the root menu's bounding box.

Menu Button Visualization

The first of the two optional horizontal menu visualizations available with the "Sam" skin renders each menu label as a menu button; each menu label is rendered with an arrow to the right of its text label, providing a visual cue that there is a corresponding submenu.

Screen capture of a horizontal menu with each menu label in the root menu rendered as menu buttons.

To apply this style to a horizontal menu, start by adding the class yui-menubuttonnav to the node representing the root menu's bounding box, as illustrated in the following example:

  1. <div id="menu-1" class="yui-menu yui-menu-horizontal yui-menubuttonnav"><!-- Bounding box -->
  2. <div class="yui-menu-content"><!-- Content box -->
  3. <ul>
  4. <!-- Menu items -->
  5. </ul>
  6. </div>
  7. </div>
  8.  
<div id="menu-1" class="yui-menu yui-menu-horizontal yui-menubuttonnav"><!-- Bounding box -->
	<div class="yui-menu-content"><!-- Content box -->
		<ul>
			<!-- Menu items -->
		</ul>
	</div>
</div>
 

Next, wrap the text node of each menu label in an <em> element:

  1. <div id="menu-1" class="yui-menu yui-menu-horizontal yui-menubuttonnav"><!-- Bounding box -->
  2. <div class="yui-menu-content"><!-- Content box -->
  3. <ul>
  4.  
  5. <li>
  6. <a class="yui-menu-label" href="#submenu-1"><em>Submenu Label</em></a>
  7. <div id="submenu-1" class="yui-menu">
  8. <div class="yui-menu-content">
  9.  
  10. <!-- submenu content -->
  11.  
  12. </div>
  13. </div>
  14. </li>
  15.  
  16. </ul>
  17. </div>
  18. </div>
  19.  
<div id="menu-1" class="yui-menu yui-menu-horizontal yui-menubuttonnav"><!-- Bounding box -->
	<div class="yui-menu-content"><!-- Content box -->
		<ul>
 
			<li>
				<a class="yui-menu-label" href="#submenu-1"><em>Submenu Label</em></a>
				<div id="submenu-1" class="yui-menu">
					<div class="yui-menu-content">
 
						<!-- submenu content -->
 
					</div>
				</div>		
			</li>
 
		</ul>
	</div>
</div>
 

As a final, optional step, use the autoSubmenuDisplay and mouseOutHideDelay configuration attributes to configure the menu labels to behave like menu buttons. Set the autoSubmenuDisplay to false, so that each menu label's submenu isn't made visible until the label is clicked. Set the mouseOutHideDelay to 0 so that a label's submenu is only hidden when the user mouses down on an area outside of the submenu.

  1. // Call the "use" method, passing in "node-menunav". This will load the
  2. // script and CSS for the MenuNav Node Plugin and all of the required
  3. // dependencies.
  4.  
  5. YUI().use("node-menunav", function(Y) {
  6.  
  7. // Use the "contentready" event to initialize the menu when the subtree of
  8. // element representing the root menu (<div id="menu-1">) is ready to
  9. // be scripted.
  10.  
  11. Y.on("contentready", function () {
  12.  
  13. // The scope of the callback will be a Node instance representing
  14. // the root menu (<div id="menu-1">). Therefore, since "this"
  15. // represents a Node instance, it is possible to just call "this.plug"
  16. // passing in a reference to the MenuNav Node Plugin.
  17.  
  18. this.plug(Y.Plugin.NodeMenuNav, { autoSubmenuDisplay: false, mouseOutHideDelay: 0 });
  19.  
  20. }, "#menu-1");
  21.  
  22. });
  23.  
//	Call the "use" method, passing in "node-menunav".  This will load the 
//	script and CSS for the MenuNav Node Plugin and all of the required 
//	dependencies.
 
YUI().use("node-menunav", function(Y) {
 
	//	Use the "contentready" event to initialize the menu when the subtree of 
	//	element representing the root menu (<div id="menu-1">) is ready to 
	//	be scripted.
 
	Y.on("contentready", function () {
 
		//	The scope of the callback will be a Node instance representing 
		//	the root menu (<div id="menu-1">).  Therefore, since "this"
		//	represents a Node instance, it is possible to just call "this.plug"
		//	passing in a reference to the MenuNav Node Plugin.
 
		this.plug(Y.Plugin.NodeMenuNav, { autoSubmenuDisplay: false, mouseOutHideDelay: 0 });
 
	}, "#menu-1");
 
});
 
Split Button Visualization

The other optional horizontal menu visualization available with the "Sam" skin renders each menu label as a split button.

Screen capture of a horizontal menu with each menu label in the root menu rendered as split buttons.

To apply this style to a horizontal menu, start by adding the class yui-splitbuttonnav to the node representing the root menu's bounding box, as illustrated in the following example:

  1. <div id="menu-1" class="yui-menu yui-menu-horizontal yui-splitbuttonnav"><!-- Bounding box -->
  2. <div class="yui-menu-content"><!-- Content box -->
  3. <ul>
  4. <!-- Menu items -->
  5. </ul>
  6. </div>
  7. </div>
  8.  
<div id="menu-1" class="yui-menu yui-menu-horizontal yui-splitbuttonnav"><!-- Bounding box -->
	<div class="yui-menu-content"><!-- Content box -->
		<ul>
			<!-- Menu items -->
		</ul>
	</div>
</div>
 

Next, define the markup for each menu label. Start with a <span> with a class of yui-menu-label applied. Inside the <span>, place two <a> elements — one for each of the label's two clickable regions. Each <a> has separate, but related responsibilities: The first <a> represents the label's default action. The second <a> toggles the display of a submenu whose content contains other options related to, or in the same category as the default action. Therefore to configure the first <a>, simply set its href attribute to any URL. For the second <a>, apply a class name of yui-menu-toggle, and set the value of the href attribute to the id of the label's corresponding submenu. Lastly, the text node of the second <a> should label the contents of its corresponding submenu.

  1. <div id="menu-1" class="yui-menu yui-menu-horizontal yui-splitbuttonnav"><!-- Bounding box -->
  2. <div class="yui-menu-content"><!-- Content box -->
  3. <ul>
  4. <li>
  5.  
  6. <span class="yui-menu-label"><!-- menu label root node -->
  7. <a href="http://answers.yahoo.com">Answers</a><!-- menu label default action -->
  8. <a href="#answers-options" class="yui-menu-toggle">Answers Options</a><!-- menu label submenu toggle -->
  9. </span>
  10.  
  11. <div id="answers-options" class="yui-menu">
  12. <div class="yui-menu-content">
  13. <ul>
  14. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answers.yahoo.com/dir/">Answer</a></li>
  15. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answersonthestreet.yahoo.com/">Answers on the Street</a></li>
  16. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answers.yahoo.com/question/;_ylt=Av3Nt22Mr7YNs651NWFv8YUPzKIX;_ylv=3?link=ask">Ask</a></li>
  17. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answers.yahoo.com/dir/;_ylt=Aqp_jJlsYDP7urcq2WGC6HBJxQt.;_ylv=3?link=over&amp;amp;more=y">Discover</a></li>
  18. </ul>
  19. </div>
  20. </div>
  21.  
  22. </li>
  23. </ul>
  24. </div>
  25. </div>
  26.  
<div id="menu-1" class="yui-menu yui-menu-horizontal yui-splitbuttonnav"><!-- Bounding box -->
	<div class="yui-menu-content"><!-- Content box -->
		<ul>
			<li>
 
				<span class="yui-menu-label"><!-- menu label root node -->
					<a href="http://answers.yahoo.com">Answers</a><!-- menu label default action -->
					<a href="#answers-options" class="yui-menu-toggle">Answers Options</a><!-- menu label submenu toggle -->
				</span>											
 
				<div id="answers-options" class="yui-menu">
					<div class="yui-menu-content">
						<ul>
							<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answers.yahoo.com/dir/">Answer</a></li>
							<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answersonthestreet.yahoo.com/">Answers on the Street</a></li>
							<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answers.yahoo.com/question/;_ylt=Av3Nt22Mr7YNs651NWFv8YUPzKIX;_ylv=3?link=ask">Ask</a></li>
							<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://answers.yahoo.com/dir/;_ylt=Aqp_jJlsYDP7urcq2WGC6HBJxQt.;_ylv=3?link=over&amp;amp;more=y">Discover</a></li>
						</ul>
					</div>
				</div>				
 
			</li>
		</ul>
	</div>
</div>
 

Use the a href="../../api/plugin.NodeMenuNav.html#config_autoSubmenuDisplay">autoSubmenuDisplay and mouseOutHideDelay configuration attributes to configure the menu labels to behave like split buttons. Set the autoSubmenuDisplay to false, so that each menu label's submenu isn't made visible until the menu trigger is clicked. Set the mouseOutHideDelay to 0 so that a label's submenu is only hidden when the user mouses down on an area outside of the submenu.

  1. // Call the "use" method, passing in "node-menunav". This will load the
  2. // script and CSS for the MenuNav Node Plugin and all of the required
  3. // dependencies.
  4.  
  5. YUI().use("node-menunav", function(Y) {
  6.  
  7. // Use the "contentready" event to initialize the menu when the subtree of
  8. // element representing the root menu (<div id="menu-1">) is ready to
  9. // be scripted.
  10.  
  11. Y.on("contentready", function () {
  12.  
  13. // The scope of the callback will be a Node instance representing
  14. // the root menu (<div id="menu-1">). Therefore, since "this"
  15. // represents a Node instance, it is possible to just call "this.plug"
  16. // passing in a reference to the MenuNav Node Plugin.
  17.  
  18. this.plug(Y.Plugin.NodeMenuNav, { autoSubmenuDisplay: false, mouseOutHideDelay: 0 });
  19.  
  20. }, "#menu-1");
  21.  
  22. });
  23.  
//	Call the "use" method, passing in "node-menunav".  This will load the 
//	script and CSS for the MenuNav Node Plugin and all of the required 
//	dependencies.
 
YUI().use("node-menunav", function(Y) {
 
	//	Use the "contentready" event to initialize the menu when the subtree of 
	//	element representing the root menu (<div id="menu-1">) is ready to 
	//	be scripted.
 
	Y.on("contentready", function () {
 
		//	The scope of the callback will be a Node instance representing 
		//	the root menu (<div id="menu-1">).  Therefore, since "this"
		//	represents a Node instance, it is possible to just call "this.plug"
		//	passing in a reference to the MenuNav Node Plugin.
 
		this.plug(Y.Plugin.NodeMenuNav, { autoSubmenuDisplay: false, mouseOutHideDelay: 0 });
 
	}, "#menu-1");
 
});
 
MenuItem Separators

The MenuNav Node Plugin makes it easy to create separators between items in a menu. To separate items in a menu, simply place items in their own <ul> elements inside their parent menu's content box, and apply a class name of first-of-type to the first <ul>. The following code will render separators between the "Zimbra" and "Address Book" items, and the "Notepad" and "Messenger" items.

  1. <div id="pim" class="yui-menu">
  2. <div class="yui-menu-content">
  3.  
  4. <ul class="first-of-type">
  5. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://mail.yahoo.com">Mail</a></li>
  6. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://www.zimbra.com">Zimbra</a></li>
  7. </ul>
  8.  
  9. <ul>
  10. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://addressbook.yahoo.com">Address Book</a></li>
  11. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://calendar.yahoo.com">Calendar</a></li>
  12. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://notepad.yahoo.com">Notepad</a></li>
  13. </ul>
  14.  
  15. <ul>
  16. <li class="yui-menuitem"><a class="yui-menuitem-content" href="http://messenger.yahoo.com">Messenger</a></li>
  17. </ul>
  18.  
  19. </div>
  20. </div>
  21.  
<div id="pim" class="yui-menu">
	<div class="yui-menu-content">	
 
		<ul class="first-of-type">
			<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://mail.yahoo.com">Mail</a></li>
			<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://www.zimbra.com">Zimbra</a></li>
		</ul>
 
		<ul>
			<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://addressbook.yahoo.com">Address Book</a></li>
			<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://calendar.yahoo.com">Calendar</a></li>
			<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://notepad.yahoo.com">Notepad</a></li>
		</ul>
 
		<ul>
			<li class="yui-menuitem"><a class="yui-menuitem-content" href="http://messenger.yahoo.com">Messenger</a></li>
		</ul>
 
	</div>	
</div>   
 

With the separator markup in place, the default "Sam" skin will render a top border between each <ul> element, as illustrated in the following screen capture of one of the MenuNav Node Plugin examples:

Screen capture of a horizontal menu

Styling State

The MenuNav Node Plugin manages the state of a menu's descendants through the application and removal of class names. Class names representing state are constructed using the descendent's name followed by a suffix representing the state. For example, hidden submenus have a class of yui-menu-hidden. Class names used to represent state are always applied to the descendant's root node. See the CSS reference for the complete list of state class names.

Active vs. Inactive

Menuitems and menu labels have a default (inactive) state and an active state. Menuitems and menu labels are considered active when the user is interacting with them via the keyboard or the mouse. The active class name applied to menuitems and menu labels has two benefits: It provides a single entry point for styling state that would otherwise require the use of multiple CSS pseudo classes (:hover and :focus). The second benefit to the use of the active class name is that it provides consistent styling of state across all types of elements in all A-Grade browsers. (IE 6 only supports the :hover and :focus pseudo classes on <a> elements. And while IE 7 supports :hover on all elements, it only supports :focus pseudo class on <a> elements.)

Visible vs. Hidden

By default all submenus are hidden and that state is reflected by the application of the yui-menu-hidden class name. If the yui-menu-hidden class name is not applied to the bounding box node of all submenus, it will automatically be applied by the MenuNav's constructor. When a submenu is visible, the yui-menu-hidden class name is removed and a class yui-menu-label-menuvisible is applied to the submenu's corresponding label.

Configuration attributes

The MenuNav Node Plugin has several configuration attributes that can be set via an object literal that is passed as a second argument to a Node instance's plug method. (Note: These attributes are case sensitive.) In the following example, the value of the mouseOutHideDelay attribute is set to 1000.

  1. var oMenuNav = Y.one("#productsandservices");
  2. oMenuNav.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 });
  3.  
var oMenuNav = Y.one("#productsandservices");
oMenuNav.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 });
 

The complete list of the MenuNav Node Plugin configuration attributes are:

Name Default Value Description
useARIA True Boolean indicating if use of the WAI-ARIA Roles and States should be enabled for the MenuNav. Set to true by default for Firefox 3 and Internet Explorer 8 as currently only these browsers have support for ARIA, and are supported by several screen readers for Windows that also offer support for ARIA.
autoSubmenuDisplay True Boolean indicating if submenus are automatically made visible when the user mouses over the menu's items.
mouseOutHideDelay 750 Number indicating the time (in milliseconds) that should expire before a submenu is hidden when the user mouses out of it.

Handling Events

The MenuNav Node Plugin publishes no custom events of its own. To listen for any DOM-related events for a menu built using the MenuNav Node Plugin, use the on method of the plugin's host Node instance. The following example illustrates how to listen for the click event of menu a built using the MenuNav Node Plugin:

  1. // Get a Node instance for HTML element
  2. var oMenuNode = Y.one("#menu-nav-1");
  3.  
  4. // Apply the MenuNav Node Plugin to the Node instance
  5. oMenuNode.plug(Y.Plugin.NodeMenuNav);
  6.  
  7. // Register a "click" event listener to the Node instance using the "on" method
  8. oMenuNode.on("click", function () {
  9.  
  10. alert("here");
  11.  
  12. });
  13.  
// Get a Node instance for HTML element 
var oMenuNode = Y.one("#menu-nav-1");
 
// Apply the MenuNav Node Plugin to the Node instance
oMenuNode.plug(Y.Plugin.NodeMenuNav);
 
// Register a "click" event listener to the Node instance using the "on" method 
oMenuNode.on("click", function () {
 
	alert("here");
 
});
 

For additional information on listening for DOM-related events, see the DOM Events section of the Node landing page.

<iframe> Shim

The MenuNav Node Plugin uses an <iframe> shim to prevent <select> elements from poking through submenus in IE 6. The <iframe> shim is only used for IE 6, and for performance its creation is deferred until the first time a submenu is made visible. Once created, the <iframe> shim is appended to the node representing a submenu's bounding box, and is therefore a sibling of the the a submenu's content box. All styling of the <iframe> is handled by the core CSS file for the MenuNav Node Plugin, so authors of custom skins won't have to worry about it. The markup template used to create each <iframe> is accessible via the SHIM_TEMPLATE property. To help users of screen readers to avoid mistakenly interacting with the <iframe> shim, its tabindex attribute is set to "-1" and its title attribute is set to "Menu Stacking Shim". The value of the <iframe>'s title attribute is available via the SHIM_TEMPLATE_TITLE property for easy localization. Lastly, the src attribute of the <iframe> is set to "javascript:false;" so that it won't load a page inside it, preventing the secure/nonsecure warning in IE when using the MenuNav Node Plugin with HTTPS.

CSS reference

Class Name Description
yui-menu Applied to the element representing a menu's bounding box.
yui-menu-content Applied to the element representing a menu's content box.
yui-menu-hidden Applied to a menu's bounding box when hidden.
yui-shim Applied to the element serving as a menu's shim.
yui-menu-horizontal Renders a menu horizontally. Applied to a menu's bounding box element.
yui-menu-label Applied to the element representing a menu's label.
yui-menu-label-active Applied to a menu's label when it is a MenuNav's active descendent.
yui-menu-label-menuvisible Applied to a menu's label when its corresponding menu is visible.
yui-menuitem Applied to the element representing a menuitem's bounding box.
yui-menuitem-content Applied to the element representing a menuitem's content box.
yui-menuitem-active Applied to a menuitem when it is a MenuNav's active descendent.
first-of-type Applied to the first <ul> element inside each menu's content box.

Known Issues

  • Open <select>-based menus poke through submenus of a menu created using the MenuNav Node Plugin

    Screen capture of an open HTML select-based menu poking through the submenu of a menu created using the MenuNav Node Plugin

    In Safari 3 and IE 6 the <select> element's popup menu is modal—making it impossible to interact with any of the other elements on the page, and therefore impossible to both trigger the display of a DHTML menu and impossible for the <select> element's popup menu to potentially poke through a DHTML menu.

    In all other browsers, a <select> element's popup menu is both not modal and has a zindex higher than any other element. The established best practice of using an <iframe> element has a shim does not fix the problem. Currently there is no fix for this issue.

Support & Community

Forums & Blog

YUI 3 discussion forums are hosted on YUILibrary.com.

In addition, please visit the YUIBlog for updates and articles about the YUI Library written by the library's developers.

Filing Bugs & Feature Requests

The YUI Library's public bug tracking and feature request repositories are located on the YUILibrary.com site. Before filing new feature requests or bug reports, please review our reporting guidelines.

Copyright © 2010 Yahoo! Inc. All rights reserved. Copyright | Privacy Policy | Terms of Use | Job Openings