Jquery UI Autocomplete Custom HTML (item is undefined)
Asked Answered
A

1

6

I've been banging my head off my desk trying to get the jQuery UI Autocomplete to output custom HTML. Here is my code.

        $(document).ready(function(){

        $.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-autocomplete-category'>" + item.category + "<ul class='autocomplete-category'></ul></li>" );
                        currentCategory = item.category;
                    }
                    self._renderItem( ul, item);
                });
            }
        });

        var data = [
            { label: "anders", category: "Antigen" },
            { label: "andreas", category: "Antigen" },
            { label: "antal", category: "Antigen" },
            { label: "annhhx10", category: "Products" },
            { 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" }
        ];

        $( "#typeAhead" ).catcomplete({
            delay: 0,
            source: data,
        })
        .data( "catcomplete" )._renderItem = function( ul, item ) {
            return $( "<li></li>" )
                .data( "item.catcomplete", item )
                .append( $( "<a class='ui-menu-item'></a>" ).text( item.label ) )
                .appendTo( $('ul').last('.autocomplete-category'));
        };
    });

I seem to be running into trouble by nesting my lists in the renderItem function. The HTML output is exactly how I want it. However when I "keydown" the I get a javascript error (item is undefined).

Any ideas or suggestions? I've tried just about everything.

Artimas answered 5/2, 2011 at 17:3 Comment(0)
S
12

You were almost there! I only made two changes to get this to work (Updated after comments from OP):

After digging into the jQueryUI autocomplete source, it looks like the underlying menu that's used by the autocomplete widget isn't too friendly toward nested elements.

I could be wrong about this, but I think the menu expects a simple <ul> with just children <li>s containing an anchor tag.

Edit: This line confirms my suspicion about the menu (found in jqueryUI 1.8.9's menu widget):

    var items = this.element.children("li:not(.ui-menu-item):has(a)")
        .addClass("ui-menu-item")
        .attr("role", "menuitem");

    items.children("a")
        .addClass("ui-corner-all")
        .attr("tabindex", -1)
        // mouseenter doesn't work with event delegation
        .mouseenter(function( event ) {
            self.activate( event, $(this).parent() );
        })
        .mouseleave(function() {
            self.deactivate();
        });

Basically, since your a tags were buried inside a nested list, they weren't getting recognized by the menu.

I bet you noticed in your original code that your autocomplete menu items were not highlighting when you moused over them. This highlighting actually coincides with which menu item the widget thinks is active, which was causing your widget to fail when the user selected an item.

Since there's nothing semantically wrong or visually wrong with just giving the category lis a different class, I would restructure the widget's menu like this:

JavaScript:

_renderItem function:

.data( "catcomplete" )._renderItem = function( ul, item ) {
    return $( "<li></li>" )
        .data( "item.autocomplete", item )
        .append( $( "<a class='ui-menu-item'></a>" )
                 .text( item.label ) )
        .appendTo(ul);
};

Your _renderMenu function:

_renderMenu: function( ul, items ) {
    var self = this,
        currentCategory = "";
    $.each( items, function( index, item ) {
        if ( item.category != currentCategory ) {
            ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
            currentCategory = item.category;
        }
        self._renderItem( ul, item);
    });
}

Generated HTML for AutoComplete menu:

<ul class="ui-autocomplete>
    <li class="ui-autocomplete-category">Antigen</li>
    <li class="ui-menu-item" role="menuitem">
         <a class="ui-menu-item ui-corner-all" tabindex="-1">anders</a>
    </li>
    <li class="ui-menu-item" role="menuitem">
        <a class="ui-menu-item ui-corner-all" tabindex="-1">andreas</a>
    </li>
    <li class="ui-menu-item" role="menuitem">
        <a class="ui-menu-item ui-corner-all" tabindex="-1">antal</a>
    </li>
    <!-- More categories, items, etc.-->
</ul>

Judging by your comments, it seemed like you wanted the menu's HTML to be nested uls inside of lis for each category. Let me know if changing the HTML of the generated menu isn't an option for you for some reason.

I've updated the example: http://jsfiddle.net/andrewwhitaker/pjs7a/2/

Hope that helps.

Suck answered 5/2, 2011 at 17:46 Comment(6)
Man, I so wanted your solution to work. Unfortunately, it doesn't :/ If you check the source code on the jsfiddle page you linked up you'll see that the item <li> isn't being added to the .autocomplete category class, it's just being added after it.Artimas
@Nick: What isn't working? Are you seeing the problem in the example I posted?Suck
Sorry, I hit enter on my last comment before I was finished typing it.Artimas
@Nick: I updated my answer, but I did change the way the menu is being created and categories are being displayed. Let me know if that doesn't work for you.Suck
@Andrew: Thanks for your help but unfortunately, that won't work for me. I need to have each of my categories in some kind of containing element so that I can float each individual category to the left. I think I might have to go with a different option than the jQuery autocomplete to accomplish the solution I need. :/Artimas
@Nick: You can still style each category a different way than the other items (including floating them left). You can fake a hierarchical display if you need to.Suck

© 2022 - 2024 — McMap. All rights reserved.