For our help desk, customer service and call center software applications,
we needed to develop a unique UI control that would allow users to select
from a complex hierarchical tree of options. We wanted this control to be
intuitive and it had to allow for both quick mouse and keyboard entry. After
many rounds of discussion internally and with customers, we came up with the following list
Menus must resize and position themselves to stay in the viewport.
Options should not show up off screen where users would need to scroll to
Quick keyboard entry. Many users prefer keyboard entry for speed of
data entry, so efficient keyboard entry is a must.
Must be able to handle very large complex (deep) data trees, but work
equally as well with very small sets of data.
While the list of requirements was not long, we knew there would be
substantial technical obstacles to overcome. The biggest obstacle to solve
would be how to ensure that the menu and all of its submenus remain on the
screen at all times.
To overcome this obstacle, we spent a lot of time developing code that would
automatically split long lists into multiple columns and then position
itself so that it always fits within the viewport. The overall effect is
somewhat similar to the Microsoft® Windows® "Start > All Programs" menu.
However, the algorithm we use should keep the menus laid out in a more
compact layout—which we believe offers quicker selection of the menu items.
The screenshot below shows what the menu looks like when used with a very
Creates a multi-column hierarchical select UI component
Binds a text input field or div element to a list element (included nested lists)
Menus are automatically split into columns as needed
Menus are positioned to always stay on the screen
Autocomplete keyboard entry (only valid options are allowed)
Menu automatically scrolls into viewport when opened
In order to use the mcDropdown plug-in, you need the following:
jQuery v1.2.6 (or higher)*
jquery.bgiframe.js Plug-in (Optional; for fixing overlay issues in IE6)
This plug-in may work with older versions of jQuery in the 1.2 family. If you try using this with an older version of jQuery, you will need to include the jquery.dimensions.js plug-in (as of jQuery v1.2.6 the jquery.dimensions.js
plug-in is included in the jQuery core.)
The mcDropdown plug-in has two main methods of being invoked. The first method creates a new instance of the widget on an existing element:
The initial element that you apply the plug-in to is destroyed and replaced with a new input element with the same id attribute.
While the mcDropdown() method does not destroy the jQuery chain, it does effectively return a
"dirty" reference (since the original element no longer exists.) Because of this, you'll want to
make sure that the mcDropdown() method is the last call in your chain. Also, if you plan on caching
a reference to the element, you will need to create the cached instance after you initiated
This argument is required and must point to an unordered list element. The list argument can be any one of the following:
A jQuery selector to an unordered list (<ul>) element
A jQuery object containing the unordered list
An unordered list DOM element
This should be a reference to a single list element.
This argument is optional and allows you to customize the settings used for each instance of the plug-in. For a list of all available options, see the Options section.
The second method of invoking the mcDropdown() plug-in is to return a reference to an existing instance of the mcDropdown() widget. So, once we have initiated an instance of the mcDropdown widget, we can do the following:
var dd = $("#mcdropdown").mcDropdown();
Now that we have a reference to the widget, we can invoke any of the public methods available.
Returns an array [value, label] containing the value stored in the hidden <input /> element and the label currently being displayed to the user.
Sets the instance of the mcDropdown widget to a specific value. The value passed should correspond the a valid list item (<li>) value.
Programmatically opens the menu.
Programmatically closes the menu. (NOTE: Any click the user makes onscreen will also close the menu.)
Calls the focus() event for the text input element.
This method will either enabled (false) or disable (true) the dropdown widget. When the widget is disabled, the user
can not change the value via the UI.
There are a number of options available for customizing the look and feel of the mcDropdown widget.
minRows: 8 // specify the minimum rows before creating a new column
, maxRows: 25 // specify the maximum rows in a column
, targetColumnSize: 2 // specify the default target column size (it will attempt
// to create this many columns by default, unless
// the min/max row rules are not being met)
, openFx: "slideDown" // the fx to use for showing the root menu
, openSpeed: 250 // the speed of the openFx
, closeFx: "slideUp" // the fx to use for hiding the root menu
, closeSpeed: 250 // the speed of the closeFx
, hoverOverDelay: 200 // the delay before opening a submenu
, hoverOutDelay: 0 // the delay before closing a submenu
, showFx: "show" // the fx to use when showing a submenu
, showSpeed: 0 // the speed of the showFx
, hideFx: "hide" // the fx to use when closing a submenu
, hideSpeed: 0 // the speed of the hideFx
, dropShadow: true // determine whether drop shadows should be shown on
// the submenus
, autoHeight: true // always uses the lineHeight options (much faster than
// calculating height)
, lineHeight: 19 // the base height of each list item (li) this is
// normally calculated automatically, but in some cases
// the value can not be determined and you will need to
// set it manually
, screenPadding: 10 // the padding to use around the border of the screen
// (this is used to make sure items stay on the screen)
, allowParentSelect: false // determines if parent items are allowed to be selected
// (by default only end nodes can be selected)
, delim: ":" // the delimited to use when showing the display string
// (must be single character)
, showACOnEmptyFocus: false // show the autocomplete box on focus when input is empty
, valueAttr: "rel" // the attribute that contains the value to use in the hidden field
, click: null // callback that occurs when the user clicks on a menu item
, select: null // callback that occurs when a value is selected
, init: null // callback that occurs when the control is fully initialized
Place the cursor in the text box and start typing, only characters that are exact matches will be registered
If the value you want is selected, press any of the following keys to either select the value or go to the next level of the tree: [ENTER], [TAB], [RIGHT ARROW] or [:] (or the character you set for the delim option.)
To delete the last typed character, press [BACKSPACE]
To go back to the parent node, press [LEFT ARROW]
Press [UP ARROW] or [DOWN ARROW] to cycle through valid options
If you do not leave the field before getting to the end node in the tree, the input element will automatically be filled in the first end node match it finds
Before you can invoke an instance of the mcDropdown widget, you must have an unordered list element—which is used as your menu. If you have ever used the "suckerfish" technique, then setting up the list element is very straight forward. One of the key features of this widget is its ability to display hierarchical data structures that are very deep. However, for this example we will keep the HTML simple and straightforward. The default CSS stylesheet Giva provides expect that all unordered lists being used as menus will have a class of "mcdropdown_menu." If you choose to use a different class name, make sure to update the CSS file accordingly.
<ul id="categorymenu" class="mcdropdown_menu">
Arts & Humanities
Business & Economy
Computers & Internet
Notice how each of the <li> elements contains the "rel" attribute? This is used to define the unique "value" for each item. This allows you to display whatever text you want to the user, but store a value that corresponds to a unique identifier. When a user selects an item from the menu, it is this value that is placed in a hidden <input /> element and therefore passed back to the server when the form is submitted. If you wish to use a different attribute to store your value, you can use the valueAttr option to change this to any attribute you want.
Now that the list element has been created, you need to create a DOM element that will become the widget:
You can invoke the mcDropdown() plug-in on any block display element (like a <div>,) but by binding it to a text input element you will enable the keyboard entry feature.
The next step is to actually create an instance of the mcDropdown widget. You want to make sure to initialize the widget after all the necessary DOM elements are available, which makes the document.ready event a great place to initialize the widget.
Click the icon to the right of the text box in order to see the widget in action. If you place the widget at the bottom of the viewport, you will notice that the widget automatically scrolls the page so that the entire menu appears on the screen. This is designed to reduce user frustration by making sure the user can actually use the control. No scrolling will take place if the widget can already display itself onscreen.
Another key usability feature the mcDropdown widget offers is keyboard entry. If you place the cursor inside the text box you can now use the keyboard to select an item. If an item is already selected, you will notice that placing focus on the input element causes the last child item to be automatically pre-selected. This allows for quickly changing the selected item if an incorrect value was entered.
We designed the keyboard entry so that only options found in the unordered list element can be selected. To accomplish this task, each keystroke is monitored and only allow valid characters are actually registered by the widget.
Also, as the user types a list of all possible matches will appear in the autocomplete list. This list is narrowed to only include exact matches. There's no need for a user to type the complete item name either, once the item the user wants is selected, they can either press [TAB], [ENTER], [RIGHT ARROW] or the [:] (which is the defined label delimiter) to select the list item. The user will then either be presented with any child items or they will move to the next position in the form.
Users can also use the [UP ARROW] and [DOWN ARROW] keys to select options that appear in the autocomplete list. See the Keyboard Usage section for more information.
This source code is provided as-is. At this time Giva is not offering direct support for this product. If you are in need of assistance, post your question to one of the jQuery Mailing Lists. Members of the Giva development team actively participate on the jQuery Mailing lists, so if we see your question we will try our best to respond.
Copyright 2011 Giva, Inc. (http://www.givainc.com/labs/)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The following download includes both uncompressed and minified versions of the plug-in and all the CSS and image files required to get you started. See the gettingstarted.htm file for usage instructions and a working example.
Fixed issue where allowParentSelect:true option would cause parent menu item to hide if clicked before sub-menu appears
Fixed support for jQuery v1.6.2
Fixed issue with blur event firing multiple times when using keyboard entry
Added ability to embed HTML into the LI item (courtesy of Denis Cabasson)
Fixed capability issues with jQuery v1.3.1 and newer
Fixed some CSS issues and reduced duplicate classes
Fixed showACOnEmptyFocus behavior
Fixed IE6 bgIframe behavior with small lists (now uses outerHeight())
Fixed IE with using the mouse to put focus in the field (cause by input element having a transparent background)
Removed the code to re-size sub-menus based on parent menu size, <li /> now rely on CSS to set a min-width
Fixed bug in which children menus would not always show correctly if the list element is in an element whose display style is set to "none". The list is now moved to the <body/> tag so that the width and visibility is controlled. (NOTE: If for some reason the <body/> tag is set to "display: none" on page load, you'll want to initialize the mcDropdown plug-in after the <body/> tag becomes visible.)
Dropped v1.2x naming and moved to v1.2.## format.
Fixed bug where cursor in input fields under the mcDropdown would disappear (due to the autocomplete div being hidden)
Fixed bug where clicking on parent item caused JS error if autocomplete dropdown had not been displayed
Added Mozilla & KHTML styles to prevent text selection in CSS file (ul.mcdropdown_menu rule)
Fixed scrollIntoView for when the dropdown is within a parent element with a relative position
Added "autoHeight" option setting this to false will force the use of the lineHeight setting which can really speed up the display of menus
Fixed scrollIntoView behavior when autocomplete list is hidden (it no longer scrolls to show the hidden list)
Added disable() method.
Added .mcdropdownDisabled CSS classes to support disabled fields.
Added "background-color: transparent;" to "div.mcdropdown input" CSS rule
Added "outline: 0;" to "div.mcdropdown a" CSS rule
Fixed code so NOR input is correctly sized if even if it's in an element that's not visible during initialization.
Fixed code so that menu runs inside of an element with relative positioning.
Fixed autocomplete box so that it properly shows up.
Fixed showACOnEmptyFocus so that tabbing to a field with a value doesn't quickly
show the autocomplete box if it doesn't belong (this was caused by the scrollIntoView() call
Added focus() method
Fixed autocomplete list from showing dropdown when go back levels in FF3
Fixed autocomplete list corruption in when go back levels after using mouse
Added tabindex="-1" to the dropdown arrow (so it shouldn't recieve focus on tabbing)
Fixed tabbing behavior so hitting [TAB] should go to the next element in the tabindex
Autocomplete no longer shows (by default) if the input is empty and recieves focus (use the setting.showACOnEmptyFocus to control this behavior)
Added setting.showACOnEmptyFocus (used for controlling whether the autocomplete list shows on focus if list is empty; default = false)
Fixed noConflict() bug (where $ wasn't being properly scoped)
Fixed Safari v3 keyboard support
Added mouse support for keyboard autocomplete box
Fixed mouse behavior. Menus should now always close after mousing off a menu option.