Downcase

Give us a call: +91-9167663006



25 July 2010

Keyboard fix when using jQuery Autocomplete in Mobile Safari Browser

I was implementing autocomplete functionality in a search form for a iphone app, which is a web mobile application developed using JQTouch and PhoneGap. The search functionality is for searching products in our system. When user enters characters in the search box, they see a drop down list of product suggestions and upon selecting one of the suggestion, I want to send them to the product page directly rather search results page. While implementing this functionality I encountered two problems, which I think most of the web mobile apps will need while making autocomplete for search box. Problem description, its analysis and solution are described below.

Note: I am using jQuery 1.3.2 and autocomplete plugin.

The Problem: Keyboard Toggle. To submit the form when user selects item from autocomplete dropdown, we use the return handler of autocomplete plugin. But it makes the keyword toggle before taking user to the result page. Let me explain it in detail :

  1. User starts typeing (using mobile keyboard) and sees product suggestions.
  2. User selects one of the options and the mobile keyboard goes off.
  3. Next, the input focus is set which shows the mobile keyboard again.
  4. Finally result handler is executed which takes user to the product page.

The keyboard toggle in step 2 and 3 is very irritating.

Reason: I dived into the autocomplete plugin to find the click event attached to the suggestion item, puts the selected value into input box and sets the focus to it. This focus causes the keyboard to toggle.

Solution: I carved out the click event and made it an option in the autocomplete plugin. You can either use the default behavior or write custom behaviors when user clicks on one of the suggestion items. Attached below is the diff of the modification to the plugin. You can save this patch and apply it to the autocomplete javascript file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
603,607c603,604
< $(target(event)).addClass(CLASSES.ACTIVE);
< select();
< // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
< input.focus();
< return false;
---
> var elem = $(target(event)); elem.addClass(CLASSES.ACTIVE);
> return (options.clickHandler && options.clickHandler(event, elem, input) || clickHandler(event, elem, input));
619a617,623
> function clickHandler(event, elem, input) {
>   select();
>   // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
>   input.focus();
>   return false;
> }
>

This patch adds option in autocomplete plugin by the name clickHandler. The suggested items provided by autocomplete plugin are list items(“li” tag) inside a div. The handler provides you three input params:

  1. event: The click event.
  2. elem: The “li” element which is clicked.
  3. input: The input element on which we have attached the autocomplete/we want the autocomplete functionality.

Also, when you click on one of the “li” items, you might want to access the data associated with it, the string being visible in dropdown options (or the JSON data if you have fetched JSON from server). To get this data (JSON or string), one can use:

1
$.data(elem[0], "ac_data")

This returns the JSON object or the string depending upon what you have associated with list “li” item.

On suggestion click, user should be directly taken to product page: The normal behavior is to take user to search results page, but we need to direct user to product page. To implement it, I fetch the product url in autocomplete request along with suggestions using JSON dataType. The autocomplete plugin supports the JSON dataType but its not documented on the plugin page. To consume JSON data, you need to tell autocomplete the value of dataType is JSON and then exploit the parse function provided by plugin, to convert the JSON data into the format the plugin accepts. Example parse function in my case is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$('#prodinput').autocomplete("/autocomplete", {
  dataType: 'json',
  parse: function(response) {
    //example response:
    //[
    //  {name:"abc", url:"/show/1"},
    //  {{name:"def", url:"/show/2"},
    //  {{name:"ghi", url:"/show/3"}
    //]
    var parsed = [];
    for (var i=0; i < response.length; i++) {
      parsed[parsed.length] = {
        data: response[i],
        value: response[i],
        result: response[i].name,
      };
    }
    return parsed;
  }
});

You need to return an array of objects, where each object should have 3 keys namely: data, value and result.

Here, is the final piece of autocomplete I used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$('#prodinput').autocomplete("/autocomplete", {
  cacheLength: 0,
  max: 100,
  width: 290,
  dataType: 'json',
  selectFirst: false,
  formatItem: function(item){
    // Make each of the item a link
    return "<a href='" + item.url + "'>" + item.name + "</a>";
  },
  parse: function(response) {
    var parsed = [];
    for (var i=0; i < response.length; i++) {
      parsed[parsed.length] = {
        data: response[i],
        value: response[i],
        result: response[i].name,
      };
    }
    return parsed;
  },
  clickHandler: function(event, elem, input) {
    elem.parent().parent().hide();
    try {
      $(input).val($.data(elem[0], "ac_data").name);
      $(input).parent().submit();
    } catch(e) {alert(e.message);}
    return true;
  }
});