Ivan Poskudnik
Ivan Poskudnik

Reputation: 91

jQuery UI Autocomplete Custom Markup

I'm using jQuery UI Autocomplete with a categorized search result and I want to spice it up with CSS.

Unfortunately, jQuery UI doesn't make it easy and the default markup is really difficult to work with:

<input class="ui-autocomplete-input"/>
<ul class="ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all">
  <li class='ui-autocomplete-category'>Category 1</li>
  <li class="ui-menu-item">
      <a class="ui-corner-all">item 1</a>
  </li>
  <li class="ui-menu-item">
      <a class="ui-corner-all">item 2</a>
  </li>
  <li class="ui-menu-item">
      <a class="ui-corner-all">item 3</a>
  </li>
  <li class='ui-autocomplete-category'>Category 2</li>
  <li class="ui-menu-item">
      <a class="ui-corner-all">item 1</a>
  </li>
  <li class="ui-menu-item">
      <a class="ui-corner-all">item 2</a>
  </li>
  <li class="ui-menu-item">
      <a class="ui-corner-all">item 3</a>
  </li>    
</ul>

The markup that I want is this:

<input class="ui-autocomplete-input"/>
<ul class="ui-autocomplete ui-catcomplete">
    <div class="ui-block">
        <div class="ui-autocomplete-category">Category 1</div>
        <div class="ui-list">
            <li class="ui-menu-item">
                <a class="ui-corner-all">item 1</a>
            </li>
            <li class="ui-menu-item">
                <a class="ui-corner-all">item 2</a>
            </li>
            <li class="ui-menu-item">
                <a class="ui-corner-all">item 3</a>
            </li>
        </div>
    </div>
    <div class="ui-block">
        <div class="ui-autocomplete-category">Category 1</div>
        <div class="ui-list">
            <li class="ui-menu-item">
                <a class="ui-corner-all">item 1</a>
            </li>
            <li class="ui-menu-item">
                <a class="ui-corner-all">item 2</a>
            </li>
            <li class="ui-menu-item">
                <a class="ui-corner-all">item 3</a>
            </li>
        </div>
    </div>
</ul>

And that's where I reach my limit.

I actually managed to create the markup, but I could no longer select and submit by clicking on the item.

Here is the code I came up with:

$.widget( "custom.catcomplete", $.ui.autocomplete, {

    _renderMenu: function( ul, items ) {
        var self = this,
            currentCategory = "",
            i = 0;

        $.each( items, function( index, item ) {
            if ( item.category != currentCategory ) {

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

            }

            self._renderItem( ul, item );
        });

        /* This is how I thought it would work */

        ul.find('.ui-autocomplete-category').each(function() {
            $(this).nextUntil('div')
                    .andSelf()
                    .wrapAll('<div class="ui-block">')
                    .nextUntil('div')
                    .wrapAll('<div class="ui-list">');
        });

    },

    _renderItem: function( ul, item) {
        return $( "<li></li>" )
        .data( "item.autocomplete", item )
        .append( "<a>" + item.label + "</a>" )
        .appendTo( ul );
    },
});

$(function() {  
    $( "#ui-autocomplete-input" ).catcomplete({
        source: '<?php echo SITEURL?>/users/complete/',
        delay: 0,
        autoFocus: true,
        select: function(event, ui) {
            if(ui.item)
            {
                $('#ui-autocomplete-input').attr('value',ui.item.value);
            }
            $('#ui-autocomplete-form').submit();
        }
    });
});

So basically, the select: function part doesn't work at all.

Could anyone help me with this?

I'm sure there is a simpler way to modify the default markup and make it work just fine.

This is what I want it to look like:

Screenshot of drop-down list with multiple rows and header on left

Upvotes: 4

Views: 7871

Answers (2)

Ivan Poskudnik
Ivan Poskudnik

Reputation: 91

I eventually found the solution by myself and it involves some CSS trickery and some simple jQuery.

CSS:

.ui-autocomplete {
    width:424px;
    margin:0;
    padding:0 0 14px 0;
}

.ui-autocomplete-category {
    width:100px;
    position: absolute;
    padding:0 20px;
    margin:20px 0 0 0;
}

.ui-menu-item {
    padding-left:140px;
}

.ui-first {
    padding-top:20px;
}

jQuery:

$( "#search" ).catcomplete({
    delay: 0,
    source: data,
    autoFocus: true,
    select: function(event, ui) {
        if(ui.item) { $('#search').attr('value',ui.item.value);}
        $('#form').submit();
        },

    /* This is the important part! */

    open: function() {
        $('.ui-autocomplete-category').next('.ui-menu-item').addClass('ui-first');
    }    
});

Basically, I play with absolute positioning for putting the category name on the left and I add a class to the first LI element with jQuery in order to create some spacing with PADDING.

It's not perfect but it works flawlessly :)

Upvotes: 4

powerbuoy
powerbuoy

Reputation: 12838

You could solve this without changing the HTML with a little bit of CSS trickery:

ul.ui-autocomplete {
    padding-left: 200px;
}

    ul.ui-autocomplete li.ui-autocomplete-category {
        width: 200px;
        height: 50px;
        margin-left: -200px;
        margin-bottom: -50px;
    }

It's not perfect (the height of the category li might exceed the height of the results - OR it might not fit the contents of the li) but it works.

Btw: the only valid child element of a ul is a li. So span wouldn't help you either.

Upvotes: 1

Related Questions