JQuery UI Autocomplete Category How to Skip Category Headers

I have an autocomplete working field in my web application, and I'm looking for a way to increase the usability of the field, somehow automatically skipping category fields when the arrow key is used to scroll through the available options (after entering a partial search).

For example, if a user starts typing "an", autocomplete displays 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 through the list. Currently, this code inserts categories into the results as a list item. When using the arrow keys, you must move them to select and select the result. In any case, the application can automatically skip the headers of these categories?

$.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); } }); 
+8
jquery-ui autocomplete jquery-ui-autocomplete categories
source share
2 answers

This line:

 ul.append( "<li class='ui-menu-item ui-category'>" + item.category + "</li>" ); 

causes a problem.

Inside, the widget uses list items with the ui-menu-item class to distinguish whether li actual menu item that can be selected. When you press the down key, the widget finds the next element with the ui-menu-item class and moves to it.

Delete the class and your code works the way you want:

 ul.append( "<li class='ui-category'>" + item.category + "</li>" ); 

Here it works:

http://jsfiddle.net/andrewwhitaker/pkFCF/

+5
source share

Since the accepted answer does not work in recent versions of jQueryUI (> 1.10.4), I will post my hack, maybe someone will find it useful.

I am using jQueryUI 1.12.0

When adding a category I added a new class, I called it "categoryItem":

 ul.append( "<li class='ui-autocomplete-category categoryItem'>" + "Category" + "</li>" ); 

Some of the jQueryUI functions also need to be redefined to force jquery to ignore elements with the categoryItem class (two lines 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 ); } })); 
+1
source share

All Articles