Click here to Skip to main content
15,881,882 members
Articles / Web Development / ASP.NET

JQueryUI smartAutocomplete

Rate me:
Please Sign up or sign in to vote.
4.75/5 (9 votes)
22 Feb 2012CPOL3 min read 82.1K   3.2K   27   20
This widget extends the functionalities of jQueryUI Autocomplete widget by adding infinite scrolling when loading data from a remote source.
jquery-smartautocomplete_1

Introduction

This widget extends the functionalities of jQueryUI Autocomplete widget by adding infinite scrolling when loading data from a remote source.

You can find the original article on my blog.

Pre-requisites

  • JavaScript, jQuery, jQueryUI
  • ASP.NET

Background

Let’s assume we are given a project that uses the Autocomplete Widget and the number of results given on a key press is so high that is affects the responsiveness of the application.

We want to be able to load only a few items initially and for every other request, load only a given number of items. So we will load the first N items and when the user reaches the last item (by scrolling to it or continually pressing key down until the last item is reached), we make another request to the server to load the next N items.

Using the Code

Let’s say we have an input:

html4strict
<input id="inp" style="width: 350px;" type="text" />

and the widget implementation:

JavaScript
$("#inp").autocomplete(options);

For more details on this code, read more at jQueryUI Autocomplete documentation website.

We want to make as few changes as possible to the code to get the desired effect of infinite scrolling. The new code would look like this:

JavaScript
$("#inp").smartautocomplete({
   getDataFunc: getData,
   pageSize: 15
   ...
});

For the purpose of this example, I have used an ASP.NET application that exposes a WebMethod that returns some dummy data to use with our input. The function looks like this:

JavaScript
//In this example I use a WebMethod, but you can call anything from a 
//local source to a web service.
var getData = function (input, pageIndex, pageSize, callback) {
   PageMethods.GetData(input, pageIndex, pageSize, function (response) {
      if (response) {
         //Data is assumed to be received in a {label: , value: , ...} form, 
         //as needed by jQueryUI Autocomplete. Of course, if you change 
         //the _renderItem function, you are free to modify this as you want
         response = $.map(response, function (item) {
              return {
            label: item,
        value: item
     }});
     callback(response);
      }
      else callback();
   });
}

As seen above, we have a source to get our data from. It can be something as a web service or anything else for a matter of fact.

The Code

We would have to create a wrapper widget that has the original autocomplete widget underneath, but handles a little bit different the data loading logic. We start by creating a jQuery plugin as instructed by jQuery plugins authoring documentation. The new widget would take two new parameters:

  1. pageSize: Number of items to load on every request
  2. getDataFunc: Function to be called that returns our data from the server

We overwrite option.source parameter with our own function that does the magic:

JavaScript
//We overwrite the source logic to call our getDataFunc function
options.source = function (request, response) {
   ...
   //We call the function to get the data
   options.getDataFunc(term, pageIndex + 1, options.pageSize, function (r) {
      if (r) {
         //We allow scrolling
         if (!r.length)
         startedLoadingFromScroll = false;
         //If data already exists, we add our new data to the existing data
         if (data.length)
            for (var i = 0; i < r.length; i++)
               data.push(r[i]);
         else data = r;
         response(data);
         //We scroll to the last position
         autocomplete.scrollTop(lastScrollTop);
         //We increment the current page index
         pageIndex++;
      }
   });
   ...
};

The next thing we do is to bind to several events triggered by the autocomplete widget. We first bind to autocompletecreate event to implement our own functionality just after the autocomplete widget has been created. Here we bind to the scroll event of the autocomplete dropdown to handle the case when we reach the end of the list. We achieve this by constantly verifying the following inequality:

C#
autocomplete[0].scrollHeight - autocomplete.scrollTop()

The result of this is true only when we have scrolled to the end of the list. If this is the case, we call our startSearching function.

JavaScript
.bind('autocompletecreate', function () {
   //We get the elements created by JqueryUI Autocomplete
   autocomplete = $(this).autocomplete('widget');
   menu = parent.data().autocomplete.menu.element;
   if (autocomplete.attr('sa-scroll') != 'on') {
      //We create the scrolling functionality to request new data 
      //when we arrived at the end of list
         autocomplete.scroll(function (e) {
         if (loading)
            return stopEvent(e);
         if (startedLoadingFromScroll) {
            if ($.browser.msie || $.browser.mozilla)
               autocomplete.scrollTop(lastScrollTop);
            return stopEvent(e);
         }
         if (autocomplete[0].scrollHeight - autocomplete.scrollTop()

Next we bind to autocompletefocus. This is called every time we navigate with the keyboard through the dropdown items. By default, autocomplete gives the focused element the class ui-state-hover. So, by verifying autocomplete.find(".ui-menu-item:last .ui-state-hover").length == 1, we are certain* the last item has focused. This is the other case when we need to load more data.

JavaScript
.bind('autocompletefocus', function (event, ui) {
   focusedItem = ui.item;
   if (ignoreFocus) {
      ignoreFocus = false;
      return;
   }
   //If we reached the last element in the list, we get new data
   if (autocomplete.find(".ui-menu-item:last .ui-state-hover").length)
   startSearching();
})

*jQueryUI has a certain way to render the list items. If we change the way items are rendered, we have to reconsider this verification.

We then bind to autocompleteopen to set focus to the last focused item before the dropdown closed.

JavaScript
.bind('autocompleteopen', function (event, ui) {
   if (!selectedIndex) {
      if (options.autoFocus) {
         ignoreFocus = true;
         menu.menu('activate', event, autocomplete.find(".ui-menu-item:first"));
      }
   }
   else {
      ignoreFocus = true;
      menu.menu('activate', event, autocomplete.find
    (".ui-menu-item:eq(" + selectedIndex + ")"));
   }
})

The last thing we do is bind to autocompleteclose to reset our search data.

JavaScript
.bind('autocompleteclose', function (event, ui) {
   //When the dropdown closes, we reset the current search
   resetData();
})

Things to Consider for Future Development

  1. This widget takes advantage of the still unpublished Menu Widget. Therefore any change in its functionality will directly affect our functionality.
  2. We base our algorithm of finding the focused item on the premise that it has the class ui-state-hover. This may not be good when customizing search results rendering system.
  3. Paging for local sources should be implemented.

History

  • 4th February, 2012: Initial version
  • 22nd February, 2012: Updated source code

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Zurply
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAnother implementation of autocomplete with PHP, MySql Pin
heightcelebs.com11-Jan-15 15:56
heightcelebs.com11-Jan-15 15:56 
Questionhow can add button that set input autocomplete from code and view result on list??? Pin
Member 195507927-Nov-14 0:42
Member 195507927-Nov-14 0:42 
QuestionIs it possible to use this on <select> list Pin
nikhil.d10-Oct-13 15:15
nikhil.d10-Oct-13 15:15 
SuggestionRemove the text if no options are found Pin
nikhil.d2-Oct-13 13:33
nikhil.d2-Oct-13 13:33 
QuestionHow to load all available items on click? Pin
nikhil.d1-Oct-13 16:52
nikhil.d1-Oct-13 16:52 
SuggestionRe: How to load all available items on click? Pin
Ovidiu Tudorache1-Oct-13 23:05
Ovidiu Tudorache1-Oct-13 23:05 
GeneralRe: How to load all available items on click? Pin
nikhil.d2-Oct-13 13:07
nikhil.d2-Oct-13 13:07 
GeneralRe: How to load all available items on click? Pin
eshan12346-Apr-15 5:56
eshan12346-Apr-15 5:56 
QuestionPost back on select Pin
sukeshchand31-Aug-13 22:12
professionalsukeshchand31-Aug-13 22:12 
Questionusing JQueryUI smartAutocomplete in mvc application Pin
zahra668826-May-13 19:42
zahra668826-May-13 19:42 
AnswerRe: using JQueryUI smartAutocomplete in mvc application Pin
Ovidiu Tudorache28-May-13 0:05
Ovidiu Tudorache28-May-13 0:05 
SuggestionRe: using JQueryUI smartAutocomplete in mvc application Pin
nikhil.d8-Oct-13 14:14
nikhil.d8-Oct-13 14:14 
GeneralMy vote of 5 Pin
Kalpesh Desai25-Jan-13 1:21
Kalpesh Desai25-Jan-13 1:21 
GeneralMy vote of 5 Pin
umeshkukreti19-Nov-12 23:29
umeshkukreti19-Nov-12 23:29 
Questionjquery ui 1.9.0 Pin
mc_tiger17-Oct-12 7:48
mc_tiger17-Oct-12 7:48 
AnswerRe: jquery ui 1.9.0 Pin
Ovidiu Tudorache18-Oct-12 0:58
Ovidiu Tudorache18-Oct-12 0:58 
AnswerRe: jquery ui 1.9.0 Pin
umeshkukreti19-Nov-12 23:31
umeshkukreti19-Nov-12 23:31 
if u r getting activate method not found error try replacing it with focus in smartautocomplete.js..
work for me..
Questionpage method is not calling Pin
arpan sharma30-Mar-12 1:17
arpan sharma30-Mar-12 1:17 
AnswerRe: page method is not calling Pin
Ovidiu Tudorache30-Mar-12 2:36
Ovidiu Tudorache30-Mar-12 2:36 
GeneralMy vote of 4 Pin
Dean Oliver6-Feb-12 4:14
Dean Oliver6-Feb-12 4:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.