I recently, spend quite a few time looking into a brand new jQuery UI widget for extensibility point in order to get a real customization.

As usual, the widget comes with a various panel of options and events, but I really wanted uncommon behaviors.

Use case

On my new website I can associate tags to my business object. I already implemented an indexer in order to retrieve tags from a simple AJAX query, I just want to add a fancy UI. Thus I added a simple text input to my form on which I want to bind my indexer via jQuery UI Autocomplete.

Here is my html:

[html]
<input type="text" id="tag-input">
<div id="tag-list">
<span>Microsoft<input type="hidden" value="1" name="object[tags_list][]"></span>
</div>
[/html]

You can see here that I placed a text input that I will “jQueryUIze” and a div that will hold my selected tags. I already put one tag into it so that you can see exactly what DOM structure I need to correctly post my form. The tag is represented using a hidden input and the held value is the Id of my tag.

A simple first implementation of the jQuery widget could be:

[js]
$("#tag-input").autocomplete({
minLength: 3,
source: function(request, response) {
$.ajax({
url: "/tag/suggestion.json",
dataType: "json",
data: { query: request.term }
});
}
});
[/js]

Here I just implemented some built-in features of the widget. When I type more than 3 characters, an AJAX request is made to my server. For example, when I type “Micro” this url is requested: /tag/suggestion.json?query=micro.

Now let’s override it !

Apply a specific data mapping

First, you need to remember that I want to share more than a simple label because I need the Id in order to correctly add it into my tag list. Also, I would like to have the requester term set to bold into my suggestion list.

Thus, here is the returned json:

[js]
{"tags":[
{
"id":"1",
"label":"Microsoft"
}
]}
[/js]

In order to correctly use the json data I need to add two modifications. First I am going to map the ajax result and then edit the renderer of the widget:

[js]
$("#tag-input").autocomplete({

//…

$.ajax({
url: "/tag/suggestion.json",
dataType: "json",
data: { query: request.term },
// 1rst need a specific mapping from my json data
success: function( data ) {
response($.map(data.tags, function(item) {
//this regex is for the bold of the suggestion list
var label = item.label.toString().replace(new RegExp(‘(‘+request.term+’)’,’i’),'<strong>$1</strong>’);
return {
// for each tag, I will retrieve a object with these 3 properties
id: item.id,
label: label,
name: item.label
}
}))
}
});

//…

//this 2nd modification is for the rendering of the suggestion list
}).data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( ‘<a href="#"><span class="tag">’ + item.label + "</span></a>" )
.appendTo( ul );
};
[/js]

You can already notice the two modifications. The first one is to translate every tag from my json into an object composed of three properties: the id for the form submission, the label for the suggestion list and the name for the form display.

The second modification overrides the item renderer. For each item it need to render, it simply creates a list item (li) on witch I add the item (we will use it later), append the proper layout (a link with a span and the label), and then add it to the suggestion list (ul).

Now I have the rendering that I expected!

After customization

Customize behaviors

Now, I want that when I select an item it flushes my search and append the selected tag to my tag list. In order to pass through this issue I will simply use the select event of the Autocomplete widget:

[js]
// when a element of the suggestion list is selected
select: function(event, ui) {
var canAppend = true;
//crawl the current tags to see if I can append it or not
$(‘#sticker-list .sticker input’).each(function(){
if($(this).val() == ui.item.id)
canAppend = false;
});
//add the tag to the tag list
if(canAppend)
$(‘#tag-list’).append(‘<span class="tag">’+ui.item.name+'<input type="hidden" name="object[tags_list][]" value="’+ui.item.id+’"></span>’);
//clean the value of the input
$(this).val("");
//return false to prevent update of the input
return false;
}
[/js]

The select event is called every time a item from the suggestion list is selected. When it occurs, I simply need to know that ui.item contains the item that constructed via the costom mapping (1rst modification) and passed trough the custom rendering to my DOM element (2nd modificcation). Now, I can check if the tag is not already in my tag list and append it.
The last thing to do is to empty the text input and prevent the default behaviour that will fill the input with the selected item. In order to do that I just nedd not to forget to return false.

And voila! We made a complete customization of the jQuery UI Autocomplete widget.

You can download the full example here : Autocomplete Customization.

2 thoughts on “Extended customization of jQuery UI Autocomplete

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s