jQuery UI Autocomplete widget search configuration
Asked Answered
E

6

65

I'm looking into using the jQuery UI autocomplete widget to implement user lookup by first or last name. It looks like by default autocomplete looks up words by character sequence no matter its occurrence in a word. So if you have data such as: javascript, asp, haskell and you type in 'as' you will get all three. I would like it to only match beginning of the word. So in above example you get only 'asp'. Is there a way to configure the autocomplete widget to do this?

Ultimately it would be even better to match by beginning of first or last name like it is in Gmail.

Note: I'm trying to figure out a way to do this using the jQuery UI widget specifically. Since I'm already using jQuery UI in my project, I'm planning to stick with it and try not adding additional libraries to my web application.

Evening answered 4/3, 2010 at 20:28 Comment(1)
henchman has a solution for gmail like search #2382997Evening
F
129

In jQuery UI v1.8rc3, the autocomplete widget accepts a source option which can be either a string, an array, or a callback function. If it's a string, autocomplete does a GET on that URL to get options/suggestions. If an array, autocomplete does a search, as you pointed out, for the presence of the typed chars in any position in the terms of the array. The final variant is what you want - the callback function.

From the autocomplete documentation:

The third variation, the callback, provides the most flexibility, and can be used to connect any data source to Autocomplete. The callback gets two arguments:

• A request object, with a single property called "term", which refers to the value currently in the text input. For example, when the user entered "new yo" in a city field that is set to do autocomplete, the request.term will hold "new yo".
• A response function, a callback which expects a single argument to contain the data to suggest to the user. This data should be filtered based on the provided term, and must be an array in the format allowed for simple local data: String-Array or Object-Array with label/value/both properties.

Example code:

var wordlist= [ "about", "above", "across", "after", "against",
                "along", "among", "around", "at", "before", 
                "behind", "below", "beneath", "beside", "between", 
                "beyond", "but", "by", "despite", "down", "during", 
                "except", "for", "from", "in", "inside", "into", 
                "like", "near", "of", "off", "on", "onto", "out", 
                "outside", "over", "past", "since", "through", 
                "throughout", "till", "to", "toward", "under", 
                "underneath", "until", "up", "upon", "with", 
                "within", "without"] ; 

$("#input1").autocomplete({
    // The source option can be an array of terms.  In this case, if
    // the typed characters appear in any position in a term, then the
    // term is included in the autocomplete list.
    // The source option can also be a function that performs the search,
    // and calls a response function with the matched entries.
    source: function(req, responseFn) {
        var re = $.ui.autocomplete.escapeRegex(req.term);
        var matcher = new RegExp( "^" + re, "i" );
        var a = $.grep( wordlist, function(item,index){
            return matcher.test(item);
        });
        responseFn( a );
    }
});

A few key points:

  • the call to $.ui.autocomplete.escapeRegex(req.term); That escapes the search term so that any regex-meaningful terms in the text typed by the user are treated as plain text. For example, the dot (.) is meaningful to regex. I learned of this escapeRegex function by reading the autocomplete source code.
  • the line with new Regexp(). It specifies a regexp beginning with ^ (Circumflex), which implies, it will match only when the typed characters appear at the beginning of the term in the array, which is what you wanted. It also uses the "i" option which implies a case-insensitive match.
  • the $.grep() utility just calls the provided function on each term in the provided array. The function in this case simply uses the regexp to see if the term in the array is a match for what was typed.
  • finally, the responseFn() is invoked with the result of the search.

working demo: http://jsbin.com/ezifi

what it looks like:

alt text

Just a note: I find the documentation on autocomplete to be pretty immature at this point. I didn't find examples that did this, and I couldn't find working doc on which .css files were necessary or which .css classes would be used. I learned all this from inspecting the code.

See also, how can I custom-format the Autocomplete plug-in results?

Fairly answered 8/3, 2010 at 21:59 Comment(7)
+1 Awesome answer! I was inspecting the code too and trying to figure out what exactly escapeRegex was for and your post explains it perfectly.Radio
Great answer. I agree with the documentation being rather immature. This was exactly what I was looking for.Xavierxaviera
Thank you so much! I was using a variable number of autocomplete (one per row with nested tables) and the speed was terrible out-of-the box even with using a js object to locally cache my data (i think the data was being copied to each table). This DRAMATICALLY sped up my app and with a caching solution ontop of this (to avoid processing the same query twice after a page load), im super happy with the results :)Grossman
To All: an Addendum regarding the "lack of maturity" in the jQuery UI documentation. Scott Gonzalez, project lead in jQuery UI doc, kindly mailed me to tell me that the content for the new jQuery UI doc site is now on GitHub, github.com/jquery/api.jqueryui.com . Anyone is free to fork and send pull requests!Fairly
Thank you for such a great answer. Can you tell how can I do this if within function source: function(req, responseFn) {... } I am making an ajax call. eg. $(function() { $("#fieldId").autocomplete({ source: function( request, response ) { $.ajax({ url: "/someUrl", dataType: "json", data: { term: request.term }, success: function( data ) { response( data ); } }); } }); }); where can I put those lines of code which modifies the regex. ThanksPatriotism
What about when your source is a json array that you get inside of a .ajax call, i.e. the call to autocomplete is INSIDE the .ajax success. So for instance I am saying source: myVariable where myVariable is a json array from $.map(data)...Petrifaction
Also myVariable looks like this [{"name":"SIK1B","organism":"Homo sapiens"},{"name":"Nep2","organism":"Drosophila melanogaster"}]Petrifaction
F
6

I use the autocomplete from devbridge. http://www.devbridge.com/projects/autocomplete/jquery/

It matches on the beginning characters, only.

alt text

As far as matching based on first name or last name, you'd just have to supply a lookup list with the first and last name.

Example usage:

  var wordlist = [
      'January', 'February', 'March', 'April', 'May', 'June',
      'July', 'August', 'September', 'October', 'November',
      'December' ]; 

      var acOptions = {
          minChars:2,
          delimiter: /(,|;)\s*/, // regex or character
          maxHeight:400,
          width:300,
          zIndex: 9999,
          deferRequestBy: 10, // miliseconds

          // callback function:
          onSelect: function(value, data){
              //$('#input1').autocomplete(acOptions);
              if (typeof data == "undefined") {
                  alert('You selected: ' + value );
              }else {
                  alert('You selected: ' + value + ', ' + data);
              }
          },

          // local autosuggest options:
          lookup: wordlist
      };

The lookup option that you pass to initialize the autocomplete control can be a list, or an object. The above showed a simple list. If you want some data attached to the suggestions that get returned, then make the lookup option an object, like this:

var lookuplist = {
    suggestions:   [ "Jan", "Feb", "Mar", "Apr", "May" ],
    data :         [ 31, 28, 31, 30, 31, ]
};
Fairly answered 4/3, 2010 at 21:2 Comment(3)
Cheeso thanks for your input, it is helpful. Since I'm already using jQueryUI plug-in in my project, I'm planning to stick with it and try not adding additional plug-ins to my web application.Evening
That makes sense. In that case you might consider making a one-off modification of the jQuery UI source, to fit your requirements. I did that to fix several bugs I encountered, that are fixed in as-yet-unreleased versions. Might make sense for you, too.Fairly
ps: I didn't realize that the autocomplete plugin was first included in jQueryUI in v1.8. I have and use v1.7.2.Fairly
G
6

thx cheeso for intrducing jsbin.com,

i extended your code to support first- and lastname matches, too.

  $("#input1").autocomplete({
      source: function(req, responseFn) {
          addMessage("search on: '" + req.term + "'<br/>");

          var matches = new Array();
          var needle = req.term.toLowerCase();

          var len = wordlist.length;
          for(i = 0; i < len; ++i)
          {
              var haystack = wordlist[i].toLowerCase();
              if(haystack.indexOf(needle) == 0 ||
                 haystack.indexOf(" " + needle) != -1)
              {
                  matches.push(wordlist[i]);
              }
          }

          addMessage("Result: " + matches.length + " items<br/>");
          responseFn( matches );
      }
  });

demo: http://jsbin.com/ufimu3/

type 'an' or 're'

Gottuard answered 8/3, 2010 at 23:45 Comment(0)
C
3

If you want to search the beginning of each word in the string, a more elegant solution to henchman's is to take cheeso's but just use the regexp word boundary special character:

var matcher = new RegExp( "\\b" + re, "i" );

Example: http://jsbin.com/utojoh/2 (try searching for 'bl')

Cogwheel answered 16/8, 2011 at 17:7 Comment(0)
P
0

There’s another jQuery autocomplete plug-in that optionally searches just at the start of each item (the option is matchContains=false, I think that’s the default too).

Given the absence of such an option in the jQuery UI plugin, I’m guessing you’ll either have to use a different plugin, or rewrite the one you’re using now.

Potentilla answered 8/3, 2010 at 15:22 Comment(3)
No, he won't have to rewrite jQueryUI to get autocomplete. There's an option to do what he wants. see my answer. #2382997Fairly
Ah, bravo. You say it’s in jQuery UI 1.8 — is that out yet?Potentilla
As far as I know jQueryUI v1.8 is listed at rc3. "Release Candidate #3". Normally an RC is a pretty stable release, pretty near final. That's normally. I don't know about jQuery's release ratings practices.Fairly
S
0

Where are you getting the data to populate the autocomplete? Is it from a database? If so, you can do what you want in your query and only return results that match the beginning of each word (first/last name)

Silverweed answered 8/3, 2010 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.