I've got a working autocomplete field in my web application and I'm looking for a way to increase the usability of the field by somehow automatically skipping the category fields when an arrow key is used to scroll down the available choices (after typing in a partial search term).
For example, if a user starts typing "an", the autocomplete will show two categories with items in each. The user wants to select one of the items in the list under "People". They use the arrow key to move down the list. Currently, this code inserts the categories in the results as a list item. When using the arrow keys, you must move passed them in order to highlight and select a result. Any way the application could automatically skip those category headers?
$.widget( "custom.catcomplete", $.ui.autocomplete, {
_renderMenu: function( ul, items ) {
var self = this,
currentCategory = "";
$.each( items, function( index, item ) {
if ( item.category != currentCategory ) {
ul.append( "<li class='ui-menu-item ui-category'>" + item.category + "</li>" );
currentCategory = item.category;
}
self._renderItem( ul, item );
});
}
});
var data = [
{ label: "annk K12", category: "Products" },
{ label: "annttop C13", category: "Products" },
{ label: "anders andersson", category: "People" },
{ label: "andreas andersson", category: "People" },
{ label: "andreas johnson", category: "People" }
];
$( "#textfield" ).catcomplete({
source: data,
select: function(event, ui) {
window.location.hash = "id_"+escape(ui.item.id);
}
});
This line:
ul.append( "<li class='ui-menu-item ui-category'>" + item.category + "</li>" );
is causing the problem.
Internally, the widget uses list items with a class ui-menu-item
to distinguish whether or not an li
is an actual menu item that can be selected. When you press the 'down' key, the widget finds the next item with a class ui-menu-item
and moves to it.
Remove the class and your code works like you want it to:
ul.append( "<li class='ui-category'>" + item.category + "</li>" );
Here it is working:
http://jsfiddle.net/andrewwhitaker/pkFCF/
Since the accepted answer doesn't work in latest versions of jQueryUI (>1.10.4) I'll post my hack, maybe someone will find it useful.
I'm using jQueryUI 1.12.0
While appending category I added new class, i called it "categoryItem":
ul.append( "<li class='ui-autocomplete-category categoryItem'>" + "Category" + "</li>" );
Some of jQueryUI functions also need to be overridden to force jquery to ignore items with "categoryItem" class (two lines are changed).
$.widget("ui.menu", $.extend({}, $.ui.menu.prototype, {
refresh: function() {
var menus, items, newSubmenus, newItems, newWrappers,
that = this,
icon = this.options.icons.submenu,
submenus = this.element.find( this.options.menus );
this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
// Initialize nested menus
newSubmenus = submenus.filter( ":not(.ui-menu)" )
.hide()
.attr( {
role: this.options.role,
"aria-hidden": "true",
"aria-expanded": "false"
} )
.each( function() {
var menu = $( this ),
item = menu.prev(),
submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
item
.attr( "aria-haspopup", "true" )
.prepend( submenuCaret );
menu.attr( "aria-labelledby", item.attr( "id" ) );
} );
this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
menus = submenus.add( this.element );
items = menus.find( this.options.items );
// Initialize menu-items containing spaces and/or dashes only as dividers
items.not( ".ui-menu-item" ).each( function() {
var item = $( this );
if ( that._isDivider( item ) ) {
that._addClass( item, "ui-menu-divider", "ui-widget-content" );
}
} );
// Don't refresh list items that are already adapted
newItems = items.not( ".ui-menu-item, .ui-menu-divider" ).not(".categoryItem");
newWrappers = newItems.children()
.not( ".ui-menu" )
.uniqueId()
.attr( {
tabIndex: -1,
role: this._itemRole()
} );
this._addClass( newItems, "ui-menu-item" )
._addClass( newWrappers, "ui-menu-item-wrapper" );
// Add aria-disabled attribute to any disabled menu item
items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
// If the active item has been removed, blur the menu
if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
this.blur();
}
},
_move: function( direction, filter, event ) {
var next;
if ( this.active ) {
if ( direction === "first" || direction === "last" ) {
next = this.active
[ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
.eq( -1 );
} else {
next = this.active
[ direction + "All" ]( ".ui-menu-item" )
.eq( 0 );
}
}
if ( !next || !next.length || !this.active ) {
next = this.activeMenu.find( this.options.items ).not(".categoryItem")[ filter ]();
}
this.focus( event, next );
}
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With