type: widget
release: 1.9
status: in development
documentation: http://docs.jquery.com/UI/Menu
demo: http://view.jqueryui.com/master/demos/menu/default.html
TODOs:
- Need better collision detection (flipfit!) in position utility. See #5284
- When you mouse over and out of a parent menu icon the open submenu position shifts up and down. The movement is <1px, but is noticeable.
- When you mouse over and out of an item with a submenu, you see a jump when the active state is removed (this happens no matter where your mouse is located, unlike #2, where the mouse is over the icon, specifically).
- this is caused by the delay on submenu closing, and the result that both ui-state-focus and ui-state-active are applied to adjacent items, causeing the -1px margin to collapse, resulting in the 1px shift
- demo: nested navigation menu
- demo: nested menu with top-alignment
- visual test: menu with checkbox, radio and text inputs
- review and address comments
1 - Description:
Transforms a (nested) list into a themeable menu with mouse and keyboard interactions for navigation.
NOTE: numbers in brackets reference visual designs.
Flat, non-hierarchical list (default when there are no nested lists)
- simple menu [D1] appears/acts like a customized dropdown list.
- popup menu [D8] appears/acts like a native Mac OS select list, where the the selected option appears boxed to the left of the trigger button, and the user can click either the selected option or button to open the menu. The menu is positioned so that the selected option always appears at the same place in the list; when the menu is open, if the selected option is third from the top, you'll see the first two options above the selection, and the rest below.
Hierarchical list:
- fly-out [D2 and D3], if the option is explicitly set or, as a best practice, if number of sub-levels limited to 2 or 3 (when 3+ submenus are showing, collision detection can become unwieldy depending on the available space in the viewport). Behaves similarly to Apple's OS menus where the main menu appears on click and sub-menus on hover; the current sub-menu remains open as long as the user's mouse is over it, otherwise it (and any other sub-menus) closes after a short delay. The user's path through the menu is marked with an "on" state.
- ipod-style/drilldown [D6 and D7], if the number of sub-levels is larger or unknown or if the option has been explicitely set. Mimics its namesake by showing a main menu, and as you choose options that navigate to deeper levels, the next menu slides to the left into view. By default a "back" link appears at the bottom of the menu, and when clicked, the visible menu slides to the right out of view. Options allow the user to:
- show a full breadcrumb in place of the back link
- change the location of the back link (above vs. below the menu)
- change the default back text (from "Back" to something else)
- set default breadcrumb text (i.e., "Choose an option:")
- select wether the menu should be context loaded, drop down or statically rendered
- menubar [E2], if the option is explicitly set (not automatic detection): the top-level menu options are arranged either horizontally or vertically in a row, and sub-menus appear either below or beside them, respectively. Sub-menus can be formatted as flat, ipod-style, or flyout. This menu could be used as standard global navigation, or as part of a toolbar. (The toolbar is a separate widget that groups navigation and form controls.)
Split buttons
Users should have the option to create split-buttons for any menu option, where the left button selects the node and the right button opens a sub-menu. The original design only spec'd split buttons for ipod style menu, but this could be useful to show link affordance in fly-outs as well.
Check and radio options
- a check or radio option is always a leaf node, never has a child menu
- check options can be toggled on/off one at a time; multiple check options can be selected/on in a single menu
- radio options are like their namesake in that you can choose one from a group. Example: EXT toolbar, "button with menu:" http://extjs.com/deploy/dev/examples/menu/menus.html
- radio options must be visually grouped together, either in a stand-alone menu or sub-menu, or set apart from other list items with a divider
- when one or more options has an check, radio, or custom icon, the entire menu -- parent menu plus all sub-menus -- is indented left so that the icons are easily scannable in a left "column"
Custom icons [D5]
Users could also specify unique icons that appear next to particular menu options.
Dividers
- dividers could be specified in the original (pre-menu widget) markup as:
- an empty list item. The menu widget could insert a horizontal rule for vertical menus, or a vertical rule for horizontal menus (menubars)
- a list item containing text (not linked) could be converted to a horizontal rule followed by a text label
- dividers/labels are not clickable
Sub-menu indicator arrows and icon alignment [D4]
- for vertical menus: Arrow icons which indicate sub-menus always appear in the right column. It may be problematic to flip the location of the arrows to the left side of the menu; doing so would only work when there are no left column check/radio icons, otherwise it may be visually noisy. Keeping the arrow icons on the right but flipping their direction to point left may also be confusing, as the arrow would be pointing back to the list item -- has anyone seen a menu behave this way? Using the Mac OS default behavior as a guide, it may be a safer bet to never flip the location or direction of the arrows and have them always stay in the right column and point right, which is the default direction of the menu, and reserve the left column for icons.
- for horizontal menus (top-level menubar buttons): An option could be set whether to show a "down" arrow to indicate sub-menus.
As decribed in the first paragraph, the behaviour/mode of the menu can be set to the following (the option for this is called 'mode'):
- dropdown, makes the element the menu is bound to to act like a button, therefore drops down the menu to one side of the original element (default is below, configurable via option 'direction')
- context, appears on right-click in a specified region by default, or the user can optionally make it appear on left click or a key + click combination [any suggestions for this option?]. The direction option can be used to set how it pops up, the default is "right below".
- static (default), simply takes the list or JSON and renders the menu statically (always visible) into the original element. If you don't specify the items option, it will search for the first ul that is a child of the selected element and transforms it automatically (this is specific to the selected mode).
One thing to consider for the grid (and many other future UI plugins) is that HTML 5 provides detailed specs for these widgets that we should seriously consider using as a blueprint for our plugins. If we shared similiar naming and conventions, it will provide a cleaner interface in the future when we all have to support both the HTML 5 version (where available) and the JS-based UI plugin verison on the same site (i.e. use the native HTML 5 if availble, fall back to JS if not supported). The other benefit is that these specs have been refined by a large group of people so they are fairly mature and there may be no need to reinvent the wheel. There is a menu spec here:
http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#menus
Many, many jQuery menu plugins already exist that we may be able to mine for ideas, or use as the basis for the jQuery widget. Several are listed below, but there may be others that look as/more promising (feel free to add to or edit this list):
Single or multi-level menus (dropdown, flyout, ipod-style)
Context menu
Enhance standard <select> into menu
http://www.givainc.com/labs/linkselect_jquery_plugin.htm
Filament flyout and iPod menus with ARIA and CSS framework support:
http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/
http://www.filamentgroup.com/examples/menus/index.html
http://filamentgroup.com/examples/menus/ipod.php
iOS-Style menu build on jQuery UI 1.9's menu widget:
http://jamesarosen.github.com/jquery-ui/demos/menu/ios-menu.html
Themeroller-based drilldown menu: http://www.matthewfeerick.com/jquery_menu/
For a very detailed description of menu design best practices and behavior, please review the Apple's UI guidelines for menus or Microsoft's Vista UI guide for menus.
2 - Visual Design:

3 - Functional Specifications/Requirements:
Options:
- position (Options or Function, default: { my: "left top", at: "right top", of: active-item } )
- How to position nested menus relative to the parent menu
- Override with any of the Position options to customize
- Override with function that returns the full set of Position options to specify the relative element. Gets the current list item as the only argument. Use of: item.parent() to get top-alignment.
Methods:
- refresh
- Renders all non-menu-items as menu-items, called once by _create
- Matches all li-elements with a child a-element, adds a class of ui-menu-item to each li that doesn't yet have that class
- Call whenever adding or replacing items in the menu via DOM operations,
e.g. via menu.append("<li><a href='#'>new item</a></li>").menu("refresh")
- focus(event, item)
- focus an item (jQuery object wrapping a li-element), by: blur the current item, scroll the new one into view if necessary, make it the focused item, and triggering the menu's focus event
- if there is no original event, pass null instead
- blur(event)
- Clear the current focus
- Useful when reopening a menu which had previously an item focused
- next(event)
- Focus the next item based on the focused one
- Focus the first if
- none is focused
- the last one is focused
- previous(event)
- Focus the previous item based on the active one
- Focus the last if
- none is focused
- the first one is focused
- nextPage(event)
- Similar to next, but jumps a whole page
- prevPage(event)
- Similar to previous, but jumps a whole page
- left(event)
- Focus a parent menu, if any.
- right(event)
- Open and focus a sub menu of the currently focused menu item, if any.
- closeAll
- Close all open sub menus and blur them.
- select(event)
- Select the focused item, triggering the select event for that item
- Useful for custom keyboard handling
Events:
- All events have ui.item refer to a jQuery object containing the focused menu item (a li-element)
- focus
- Triggered when a menu item gets mouse (on hover) or keyboard (navigation with cursor keys) focus
- blur
- Triggered when a menu item loses focus
- select
- Triggered when a menu item was selected
There is no API for binding data to each item that menu provides. Instead, jQuery's data API can be used to bind whatever necessary to each menu item and retrieve that within the select-event via ui.item. Autocomplete does exactly that.
Delaying opening and closing submenus
When hovering a menu item with a submenu, slightly (200-300ms) delay opening the submenu. Close open (sibling) submenus when opening another submenu.
Implementation-wise: Start a timeout on mouseover. Clear the timeout on mouseout. When it executes, close open submenu and open the new submenu.
Icons
Icons are supported by adding a class of ui-menu-icons to the menu element, which happens automatically when there is an element within the menu with the class ui-icon. The ui-menu-icons class adds some padding to the left to offset menu items (with or without icon). The icon generated for submenus isn't affected and will continue to be positioned on the right.
4 - Markup & Style:
4.1 Initial markup examples
Any unordered list:
<ul>
<li><a href="http://google.com">Google</a></li>
<li><a href="http://yahoo.com">Yahoo</a></li>
<li><a href="http://msn.com">MSN</a></li>
<li><a href="http://ask.com">Ask</a></li>
<li><a href="http://aol.com">AOL</a></li>
</ul>
For nested menus, nested unordered lists:
<ul>
<li><a href="http://google.com">Google</a></li>
<li><a href="http://yahoo.com">Yahoo</a></li>
<li><a href="http://msn.com">MSN</a>
<ul>
<li><a href="/htp://sub1.example.com">sub-level</a></li>
<li><a href="http://sub2.example.com">sub-level</a></li>
</ul>
</li>
<li><a href="http://ask.com">Ask</a></li>
<li><a href="http://aol.com">AOL</a></li>
</ul>
4.2 Recommended transformed HTML markup
4.3 Accessibility recommendations
Set role="listbox" on menu container (usually ul).
Set aria-activedescendant attribute with the value referring to the current active item's id-attribute, which, if not present, gets generated based on the menu's id and a "-activedescendent" postfix.
Set role=menuitem to items in the menu, and add aria-haspopup=true for items that have a submenu.
4.4 CSS & Theme
See implemented jquery.ui.menu.css
5 - Latest version of plugin:
http://view.jqueryui.com/master/tests/visual/menu/menu.html
6 - Open issues being discussed
API Challenges
While working on the iOS-style version of the menu, I found I needed to redefine several key methods:
- #left - the default behavior simply hides the menu and focuses on the parent; I needed to slide the child out.
- #_open - the default behavior shows and positions the child menu; I needed to position it, then slide it in.
- #select - I added back links to the child lists and thus had to override #select to call #left instead of triggering a select event
- #_startOpening - the default behavior is to show a submenu when hovering over (or focusing via keyboard navigation) its name; this doesn't work for the iOS-style menu, which relies on an active press (mouse or keyboard) to open a menu
In general, this can all be overcome by splitting out behavior into smaller helper methods that are configurable via options(). I rather like the SproutCore- and Cocoa style of willFoo() and didFoo() callbacks. For example, we could add a willMoveLeft(event, parent, child) and didMoveLeft(event, parent, child) callbacks. If willMoveLeft returns false, the move is cancelled. [James]
The jQuery UI pattern for naming cancelable and notification events (like this) is beforemoveleft and moveleft. For example, see Dialog's beforeclose and close. [Richard]
Comments (12)
Rick Waldron said
at 3:12 pm on Mar 28, 2011
https://gist.github.com/891168 event object inside of blur event callback is `undefined`
ajpiano said
at 5:14 pm on Mar 28, 2011
The "left" and "right" methods are somewhat unintuitively named, I'd suggest using "parent" and "sub" instead.
Jörn Zaefferer said
at 4:09 am on Mar 29, 2011
How about parent and child?
Maggie Wachs said
at 2:38 pm on Apr 21, 2011
Regarding issue #3, I think we're assigning the active state class prematurely. Currently the sequence of events is (roughly):
mouseover parent item (with a submenu)
focus class is assigned to parent
submenu opens
active class is also assigned to parent, which overrides focus class styles.
The active class should be assigned to the parent only when the user focuses on a submenu item, not when the submenu itself is opened. When these classes are assigned at the same time, the parent item appears to lose it's focus state when it's still the focused item, which is confusing. (And, conveniently, when we show the correct feedback and make the assignment of these classes mutually exclusive, the jump goes away.)
Jörn Zaefferer said
at 10:41 am on Apr 23, 2011
Just updated the implementation to add ui-state-active on focus of submenu items. That improves the interaction a lot, but doesn't fully solve the 1px jumping issue.
I can still produce it by focusing on a submenu, then hovering the adjacent item to the parent active item. In that case, focus and active states are next to each other again.
Removing ui-state-active on blur doesn't seem to be a good solution here, as that is triggered whenever you mouseout of a submenu. Any suggestions on how to tackle that?
Also, despite the fix for the icon-hover jumping, I still see that issue in Firefox 4.
Maggie Wachs said
at 11:40 am on May 5, 2011
Hi Jörn,
I spent some time testing the menu this morning, and though I wasn't able to pinpoint the where the jump is happening in the code, when you mouse out of a sub menu, the focus class is quickly (almost simultaneously) added and removed from the active parent item. I think the parent item should retain focus until the user clicks/tabs away -- or at least that's how Mac menus work.
Todd, any thoughts on this?
Todd Parker said
at 12:13 pm on May 5, 2011
I agree that the parent should maintain focus, yes.
marceljackwerth said
at 7:23 am on Apr 23, 2011
The current implementation is very fixed on a pure UL/LI representation. That's ok for a pure menu, but ui.autocomplete now depends on this menu. It's impossible to create a style like Last.fm's autocomplete / a style better suited for a use-case. I would like to see more decoupling from the UL/LI structure so that menu becomes easily modifiable for specific use-cases (similar to what ui.autocomplete was in the original 1.8 release) via $.widget.
http://forum.jquery.com/viewFile.do?fileId=14737000002219281&forumGroupId=14737000000003003 is an example of what I wanted to achieve (and achieved) by extending the ui.menu behavior. Some changes were necessary. They can be found here: https://github.com/sirlantis/jquery-ui/commit/6222205679c312a8b1cbffcc249ee4be331a875b
Main changes are the introduction of a few private functions: _findSubmenus, _prevItems, _nextItems, _menuItems which replace inline queries like .children(".ui-menu-item"), .parent(), or .prevAll(). I might have missed a few inline queries in my commit - but I would be willing to find and update them as well.
But first the question has to be answered: Is that the direction where the Menu should go (be extensible, be the base for different visuals) or should Menu remain just one of the many solutions to make a UL/LI behave like a menu? Based on the blog-post http://blog.jqueryui.com/2011/03/api-redesigns-the-past-present-and-future/ the first should be the way to go.
Scott González said
at 7:16 pm on Apr 24, 2011
I've mentioned to Jörn a few times that I'd like to see menu work with arbitrary markup structures, but I'm not sure where we stand with that. I assume Jörn is waiting until all of the existing issues are resolved before contemplating how to support other markup structures.
Kris Borchers said
at 10:05 am on Jun 10, 2011
OK, I have a first pass at check and radio functionality in menu if someone would like to take a look. https://github.com/kborchers/jquery-ui/blob/974a7ad4db7d265f789e3be62e1924a7ae6f9c03/tests/visual/menu/inputmenu.html Any feedback would be appreciated, including "You're doing it wrong", as I would like to contribute more to jQuery UI but want to do it right.
Jörn Zaefferer said
at 10:58 am on Jun 10, 2011
Just took a look. The demo is broken, due to broken script references, like: ../../../master/ui/jquery.ui.menu.js - that should just be "../../../ui/jquery.ui.menu.js" instead.
When fixing that, the result looks quite nice. Close to the visual designs here, which makes me wonder if those are actually too subtle, its kind of hard to recognize these as inputs. Not a problem of the test, more of the design itself.
In terms of the code, could you look into user this._super("refresh") in place of duplicating the menu refresh code? Caching $( item ).parent() would be a good idea, too. The hr should get some custom style to adapt the default ridge/groove style to something that matches the rest of the menu.
In terms of the markup itself, we need to explore how to embed actual inputs into the list, and enhancing that, instead of just adding radio and check classes to list items. Should start with a properly marked up form that get enhanced.
Keep going!
Kris Borchers said
at 2:39 pm on Jun 10, 2011
OK, new version here using inputs and cleaned some things up. Let me know what you think. https://github.com/kborchers/jquery-ui/blob/5b55c18b6b4053a24197ea0048a067f66bd46944/tests/visual/menu/inputmenu.html
You don't have permission to comment on this page.