7

I need a simple autocomplete search functionality but also allowing users to type more than one value. I'm using jQuery UI's autocomplete widget (http://jqueryui.com/autocomplete/) and so far I've set the source to only search for the first letter in the suggestions. What I'd like to add now is the ability for users to search for multiple items from the same textbox. (i.e. after a comma suggestions are shown again)

I have been trying to search on how this could be done. The only thing I've managed to find is an option that could be added multiple: true (http://forum.jquery.com/topic/multiple-values-with-autocomplete). Thing is that it's not even listed in the documentation anymore so I don't know if the option has changed or doesn't exist anymore.

This is my code:

    var items = [ 'France', 'Italy', 'Malta', 'England', 
        'Australia', 'Spain', 'Scotland' ];

    $(document).ready(function () {
        $('#search').autocomplete({
            source: function (req, responseFn) {
                var re = $.ui.autocomplete.escapeRegex(req.term);
                var matcher = new RegExp('^' + re, 'i');
                var a = $.grep(items, function (item, index) {
                    return matcher.test(item);
                });
                responseFn(a);
            }
        });
    });

What I tried:

    var items = [ 'France', 'Italy', 'Malta', 'England', 
        'Australia', 'Spain', 'Scotland' ];

    $(document).ready(function () {
        $('#search').autocomplete({
            source: function (req, responseFn) {
                var re = $.ui.autocomplete.escapeRegex(req.term);
                var matcher = new RegExp('^' + re, 'i');
                var a = $.grep(items, function (item, index) {
                    return matcher.test(item);
                });
                responseFn(a);
            },
            multiple: true
        });
    });

3 Answers 3

13

Try this:

  function split( val ) {
    return val.split( /,\s*/ );
  }

  function extractLast( term ) {
     return split( term ).pop();
   }

   $( "#search" )
        .autocomplete({
             minLength: 0,
             source: function( request, response ) {
                 response( $.ui.autocomplete.filter(
                     items, extractLast( request.term ) ) );
             },
             focus: function() {
                 return false;
             },
            select: function( event, ui ) {
                var terms = split( this.value );
                terms.pop();
                terms.push( ui.item.value );
                terms.push( "" );
                this.value = terms.join( ", " );
                return false;
            }
        });

SEE DEMO

Sign up to request clarification or add additional context in comments.

3 Comments

Awesome that works! :) Only problem I have is that I can't seem to get it to work with the RegEx I have in the above code. Right now it's ignoring the RegEx
It is correct, however it's not working with the RegEx I have. I'd like the suggestions to be matched with the first letter of the search not 'letter-by-letter'. That's the only issue I have. If you look at the code I've posted I have a RegEx to cater for this. Even though your code works, when I add the RegEx code I have above suggestions are only shown for the first word.
Is there a way we can fetch the data from database instead of using the predefined list. Thanks in advance.
5

To solve the issue of multiple strings in the same textbox AND include a regex to only show suggestions matching the start of the string I did the following:

    $('#search').autocomplete({
        minLength: 1,
        source: function (request, response) {
            var term = request.term;

            // substring of new string (only when a comma is in string)
            if (term.indexOf(', ') > 0) {
                var index = term.lastIndexOf(', ');
                term = term.substring(index + 2);
            }

            // regex to match string entered with start of suggestion strings
            var re = $.ui.autocomplete.escapeRegex(term);
            var matcher = new RegExp('^' + re, 'i');
            var regex_validated_array = $.grep(items, function (item, index) {
                return matcher.test(item);
            });

            // pass array `regex_validated_array ` to the response and 
            // `extractLast()` which takes care of the comma separation

            response($.ui.autocomplete.filter(regex_validated_array, 
                 extractLast(term)));
        },
        focus: function () {
            return false;
        },
        select: function (event, ui) {
            var terms = split(this.value);
            terms.pop();
            terms.push(ui.item.value);
            terms.push('');
            this.value = terms.join(', ');
            return false;
        }
    });

    function split(val) {
        return val.split(/,\s*/);
    }

    function extractLast(term) {
        return split(term).pop();
    }

Comments

0

If you want to implement the focus function instead of returning false, it's:

focus: function (event, ui) {
    var terms = split(this.value);
    terms.pop();
    terms.push(ui.item.value);
    this.value = terms.join(', ');
    return false;
},

If you do this though, you should probably extract commas from the values.

Also, you can replace lines 7-10 with a call to extractLast. And then you can get rid of your other extractLast because you already called it on term.

With all my changes:

$('#search').autocomplete({
    minLength: 1,
    source: function (request, response) {
        var term = extractLast(request.term),
            re = $.ui.autocomplete.escapeRegex(term),
            matcher = new RegExp('^' + re, 'i'),
            regex_validated_array = $.grep(items, function (item, index) {
                return matcher.test(item);
            }),
            mapped_array = regex_validated_array.map(function (item) {
              return value.replace(/,/g, '');
            });
        response($.ui.autocomplete.filter(mapped_array, term));
    },
    focus: function () {
        var terms = split(this.value);
        terms.pop();
        terms.push(ui.item.value);
        this.value = terms.join(', ');
        return false;
    },
    select: function (event, ui) {
        var terms = split(this.value);
        terms.pop();
        terms.push(ui.item.value);
        terms.push('');
        this.value = terms.join(', ');
        return false;
    }
});

function split(val) {
    return val.split(/,\s*/);
}

function extractLast(term) {
    return split(term).pop();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.