Click here to Skip to main content
Click here to Skip to main content

JQueryUI smartAutocomplete

, 22 Feb 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

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

and the widget implementation:

$("#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:

$("#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:

//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:

//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:

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.

.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.

.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.

.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.

.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)

Share

About the Author

Ovidiu Tudorache
Chief Technology Officer Zurply
Romania Romania
No Biography provided

Comments and Discussions

 
QuestionIs it possible to use this on <select> list [modified] Pinmembernikhil.d10-Oct-13 15:15 
SuggestionRemove the text if no options are found [modified] Pinmembernikhil.d2-Oct-13 13:33 
QuestionHow to load all available items on click? PinmemberNikhil_Deshpande1-Oct-13 16:52 
SuggestionRe: How to load all available items on click? PinmemberOvidiu Tudorache1-Oct-13 23:05 
GeneralRe: How to load all available items on click? PinmemberNikhil_Deshpande2-Oct-13 13:07 
QuestionPost back on select Pinmembersukeshchand31-Aug-13 22:12 
Questionusing JQueryUI smartAutocomplete in mvc application Pinmemberzahra668826-May-13 19:42 
AnswerRe: using JQueryUI smartAutocomplete in mvc application PinmemberOvidiu Tudorache28-May-13 0:05 
Replace PageMethod call with an AJAX call to your action. You can find the URL of the action using URL.Action helper method. If you POST the call, don't forget to serialize the data (use JSON2's stringify function, for example).
SuggestionRe: using JQueryUI smartAutocomplete in mvc application [modified] Pinmembernikhil.d8-Oct-13 14:14 
GeneralMy vote of 5 PinmemberKalpesh Desai25-Jan-13 1:21 
GeneralMy vote of 5 Pinmemberumeshkukreti19-Nov-12 23:29 
Questionjquery ui 1.9.0 Pinmembermc_tiger17-Oct-12 7:48 
AnswerRe: jquery ui 1.9.0 PinmemberOvidiu Tudorache18-Oct-12 0:58 
AnswerRe: jquery ui 1.9.0 Pinmemberumeshkukreti19-Nov-12 23:31 
Questionpage method is not calling Pinmemberarpan sharma30-Mar-12 1:17 
AnswerRe: page method is not calling PinmemberOvidiu Tudorache30-Mar-12 2:36 
GeneralMy vote of 4 PinmemberDean Oliver6-Feb-12 4:14 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.141022.2 | Last Updated 22 Feb 2012
Article Copyright 2012 by Ovidiu Tudorache
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid