jQuery ui autocomplete when user does not select an option from the dropdown
Asked Answered
J

13

30

When using the jquery autocomplete plugin, what do you do when the user does not select an item in the list, but instead types a valid value and tabs away?

eg when the auto complete list contains:

Cat
Dog
Fish 

And the user types cat, but does not select Cat from the autocomplete's dropdown list and instead tabs away. Because they did not select any item from the list, the autocomplete select event does not fire, and we lose the chance to respond to it:

$('#Animal').autocomplete({
    source: url,
    minlength: 1,
    select: function (event, ui) {
        $("#Animal").val(ui.item.value);
        changeUsersAnimal(ui.item.id);
    }
});

How do I handle this case?

Johathan answered 1/5, 2012 at 23:51 Comment(2)
Note that cat and Cat aren't the same thing.Appointive
Use the autoFocus option of the autocomplete. See more details in my answer below.Nesta
P
19

You're probably looking for Scott González' autoSelect extension. Just including this extension on the page will allow the select event to fire if the user enters a valid value and should require no changes on your end:

/*
 * jQuery UI Autocomplete Auto Select Extension
 *
 * Copyright 2010, Scott González (http://scottgonzalez.com)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * http://github.com/scottgonzalez/jquery-ui-extensions
 */
(function( $ ) {

$.ui.autocomplete.prototype.options.autoSelect = true;
$( ".ui-autocomplete-input" ).live( "blur", function( event ) {
    var autocomplete = $( this ).data( "autocomplete" );
    if ( !autocomplete.options.autoSelect || autocomplete.selectedItem ) { return; }

    var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" );
    autocomplete.widget().children( ".ui-menu-item" ).each(function() {
        var item = $( this ).data( "item.autocomplete" );
        if ( matcher.test( item.label || item.value || item ) ) {
            autocomplete.selectedItem = item;
            return false;
        }
    });
    if ( autocomplete.selectedItem ) {
        autocomplete._trigger( "select", event, { item: autocomplete.selectedItem } );
    }
});

}( jQuery ));

Here's an example using the extension: http://jsfiddle.net/vFWUt/226/

Pirog answered 2/5, 2012 at 0:6 Comment(13)
Promising .. looking at it nowJohathan
The problem I see in the extension is that it uses live which will be fire on every blur in the page. thumbs down for the owner. upvote for you...!Appointive
@gdoron: That's true. You could tweak it to use on instead and I believe it would work except for autocompletes applied after the initial page load.Pirog
+1 Works beautifully and I don't have to go through and update all 74 autocompletes :)Johathan
@Appointive the event should only fire for the autocomplete as I understand it: $( ".ui-autocomplete-input" ).live("blur" -> will only fire for anything with the ui-autocomplete-input class, or is this understanding not correct?Johathan
@JK.. read the live page on the jQuery site. you don't know how delegate events work. Good luck reading... :)Appointive
I tried this with an autocomplete field and then added another text field to my page and the blur event only fires for the autocomplete field.Ekaterinodar
For anyone looking for this extension (VERY HELPFUL BTW). The URL has changed to: github.com/scottgonzalez/jquery-ui-extensions/blob/master/src/…Movable
See answer by @Nesta below re: AutoFocus property. I almost used this answer's code, but the AutoFocus property is supported by default in UI 1.8.11+.Bub
@MattBeckman: Actually, the autoFocus option and this answer are not equivalent. If the user types an answer and then clicks away with autoFocus:true, the item will not be selected. Using this extension the item will be selected no matter how the user changed focus. I suppose if you take the question literally then yes, autoFocus satisfies the requirement, but personally I find the fact that it doesn't handle the "click out" case a bit frustrating.Pirog
Unfortunately this doesn't work if you click outside the field (triggering the blur event) before the list of suggestions comes up.Jariah
I get autocomplete.options is undefined error here, which is odd.Phillada
@SolomonClosson: This answer is 4 years old now, the plugin might not work anymore :-/Pirog
N
17

With jQuery version >= 1.8.11 use the autoFocus option set to true

$( ".selector" ).autocomplete({ autoFocus: true });

That has the additional advantage of automatically selecting the first item in the list so the user can just press Enter or Tab to select it without having to type all the name.

Nesta answered 30/7, 2013 at 16:24 Comment(3)
autoFocus doesnt force to select the item. So this is not the answer.Deglutition
@ChristophMeißner: the question is: what do you do when the user does not select an item in the list, but instead types a valid value and tabs away? If you set autoFocus to true, the first item matching what the user typed will be selected. I think that is the desired behavior.Nesta
Even with autoFocus set to true, the user can type or paste a valid item name and then leave the field fast enough that the autocomplete list hasn't been displayed yet, leaving the field without a selected item. This can also happen if the autocomplete list is displayed, but the user moves the mouse and causes the first item in the list to be deselected before leaving the field without selecting an item. This problem is what Scott González's Auto Select extension attempts to correct, by watching the field's blur event and then forcing an item to be selected if it matches.Crist
A
7

Add a custom event of onchange

$('#Animal').change(function(){
    var selectedValue = this.value;
    // Do what you want here:
    ...
});

Or use the built-in change event of the widget:

$('#Animal').autocomplete({
    source: url,
    minlength: 1,
    select: function (event, ui) {
        $("#Animal").val(ui.item.value);
        changeUsersAnimal(ui.item.id);
    }
   change: function(event, ui) { // <=======
       // ... 
       // ...
   }
});

source

Appointive answered 1/5, 2012 at 23:57 Comment(9)
That's specific to just one single autocomplete - I have 50+ autocompletes to go through and fix. Is there a more generic way?Johathan
@JK.. Give them all a class and grab them with a class selector. Cool?Appointive
That is quite awkward because we need to know the item.id value when performing the onselected action. If just use .change() then we dont have the id and need to do something like $(selector).change(function(){ var selectedValue = this.value; var id=GetIdFromName(selectedValue); doSelectChangeEvent(id); });. So this is means writing an extra function and having an extra ajax call for every autocomplete.Johathan
autocompletechange won't have a value for ui.item if the user didn't select an item from the menu.Pirog
Using the change event sounds like an improvement, but as @AndrewWhitaker points out, there is no selected item to refer toJohathan
@Johathan do you not know how to use $(this).attr('id') inside the change function? To get the value of the input element, you can do: $(this).val()Phillada
So, inside the change function, can just do this: if (!ui.item) alert($(this).attr('id'));Phillada
this.value, this.id. Don't use overdose jquery... it's even more to type (not to mention less efficient) @SolomonClossonAppointive
Ok, sure thing @gdoron, but it's all the same at the end of the day anyways. Cheers :)Phillada
S
6

For jQuery UI 1.9.2 I had to change a bit Scott González' autoSelect extension in Andrew Whitaker's answer:

var item = $( this ).data( "item.autocomplete" );

to be

var item = $( this ).data( "uiAutocompleteItem" );

and then it works perfectly.

Shortlived answered 4/1, 2013 at 16:4 Comment(0)
I
4

You can use like this

$("#inputbox").autocomplete({
    source : reuesturl,
    minLength : 1,
    select : function(event, ui) {
        $("#inputbox").attr('rel',ui.item.label);
    },
    open : function() {
        $("#inputbox").attr('rel', 0);
    },
    close : function() {                    
        if ($("#inputbox").attr('rel')=='0')
            $("#inputbox").val('');
    }
});
Incarnate answered 27/9, 2014 at 10:26 Comment(0)
S
2

For jQuery UI - v1.11.0 I had to change a bit Scott González' autoSelect extension as below.

/*
 * jQuery UI Autocomplete Auto Select Extension
 *
 * Copyright 2010, Scott González (http://scottgonzalez.com)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * http://github.com/scottgonzalez/jquery-ui-extensions
 */
$().ready(function () {
    $.ui.autocomplete.prototype.options.autoSelect = true;
    $(".ui-autocomplete-input").change(function (event) {
        var autocomplete = $(this).data("uiAutocomplete");

        if (!autocomplete.options.autoSelect || autocomplete.selectedItem) { return; }

        var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val()) + "$", "i");
        autocomplete.widget().children(".ui-menu-item").each(function () {
            var item = $(this).data("uiAutocompleteItem");
            if (matcher.test(item.label || item.value || item)) {
                autocomplete.selectedItem = item;
                return false;
            }
        });

        if (autocomplete.selectedItem) {
            autocomplete._trigger("select", event, { item: autocomplete.selectedItem });
        }
    });
});

And had to apply extended autocomplete option autoSelect: true in my autocomplete definition.

I took a bit of code from these answers.

  1. trushkevich
  2. gdoron and
  3. Cagatay Kalan

EDIT

Here's my autocomplete definition, in case someone is interested.

$("your selector").autocomplete({
    // Below filter looks for the values that start with the passed in string
    source: function (request, response) {
        var matches = $.map(yourSourceArray, function (acItem) {
            if (acItem.toUpperCase().indexOf(request.term.toUpperCase()) === 0) {
                return acItem;
            }
        });
        response(matches);
    },
    // one can directly pass the source array instead like,
    // source: yourSourceArray
    autoSelect: true,
    autoFocus: true,
    minLength: 3,
    change: function (event, ui) {
        if (ui.item) {
            // do whatever you want to when the item is found
        }
        else {
            // do whatever you want to when the item is not found
        }
    }
})
Sommersommers answered 25/7, 2014 at 14:5 Comment(0)
Z
1

The following code is a little tweak on Scott's extension to work with jquery ui 1.10.3 version, I have tested the below code with 1.10.3 version only.

(function($) {

$.ui.autocomplete.prototype.options.autoSelect = true;
$( ".ui-autocomplete-input" ).live( "blur", function( event ) {
    var autocomplete = $( this ).data( "ui-autocomplete" );
    if ( ! autocomplete.options.autoSelect || autocomplete.selectedItem ) { return; }

    var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" );
    autocomplete.widget().children( ".ui-menu-item" ).each(function() {
        var item = $( this ).data( "ui-autocomplete-item" );
        if ( matcher.test( item.label || item.value || item ) ) {
            autocomplete.selectedItem = item;
            return false;
        }
    });
    if ( autocomplete.selectedItem ) {
        autocomplete._trigger( "select", event, { item: autocomplete.selectedItem } );
    }
});
}(jQuery));
Zo answered 19/6, 2013 at 6:54 Comment(0)
S
1

Another simple trick is to use .autocomplete({ focus: and change: }). On focus put data in element data attribute, on change take it:

return $(element).autocomplete({
            source: get_db_cities,
            select: function (event, ui) {
                $(this).attr('tz',ui.item.tz);
            },
            autoFocus: true,
            focus: function (event, ui) {
                $(this).attr('tz',ui.item.tz);
                $(this).attr('label',ui.item.label); // put data in attribute
            },
            change: function () {
                $(this).val($(this).attr('label')); // take from attribute and insert as value
            }
});
Standpipe answered 4/5, 2021 at 10:41 Comment(0)
S
0

This code only autoselects once. All others after that do nothing. Any ideas?

Edit: I commented out this line and it now works. Don't know why.

if ( !autocomplete.options.autoSelect || autocomplete.selectedItem ) { return; }
Spectroscope answered 13/6, 2013 at 17:24 Comment(1)
Rather than commenting the line; add new option in your autocomplete as autoSelect: true. And this line won't create a problem for you.Sommersommers
E
0

I was having trouble with this function in a page using jquery 1.9.1 and jquery-ui 1.10.3. Based on the Scott Gonzalez' code and the suggestions here and a few hours of thrashing, I came up with the following. Note, I wanted a solution where the user is only allowed to enter one of the values suggested by the autocomplete -- but I wanted to allow the case where the user just types in one of the allowed values without selecting it from the drop down:

/*
 * jQuery UI Autocomplete Auto Select Extension
 *
 * v 1.10
 * Jomo Frodo ([email protected])
 * 
 * This version requires an autoSelect parameter to be set on the autocomplete widget
 * 
 * e.g.,
 *      $("#autoCompleteID").autocomplete({
            source:url,
            minLength:1,
            autoSelect: true
        });
 * 
 * Based on an extension by Scott González (http://scottgonzalez.com) 
 * http://blog.jqueryui.com/2010/08/extensible-autocomplete/
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * http://github.com/scottgonzalez/jquery-ui-extensions
 */

$(window).load(
        function() {

            //$.ui.autocomplete.prototype.options.autoSelect = true; 
            // Doesn't appear to work in ui 1.10.3
            // Must set the 'autoSelect' param on the autocomplete widget to get this to work.

            $(".ui-autocomplete-input").bind('autocompleteresponse',
                    function(event, ui) {
                        $(this).data('menuItems', ui.content);
                    });

            $(".ui-autocomplete-input").on(
                    "blur",
                    null,
                    function(event) {
                        var autocomplete = $(this).data("uiAutocomplete");
                        if (!autocomplete.options.autoSelect
                                || autocomplete.selectedItem) {
                            return;
                        }

                        var matcher = new RegExp("^"
                                + $.ui.autocomplete.escapeRegex($(this).val())
                                + "$", "i");
                        var menuItems = $(this).data('menuItems');
                        for (idx in menuItems) {
                            var item = menuItems[idx];
                            if (matcher.test(item.value)) {
                                autocomplete.selectedItem = item;
                                break;
                                // return false;
                            }
                        }
                        if (autocomplete.selectedItem) {
                            autocomplete._trigger("select", event, {
                                item : autocomplete.selectedItem
                            });
                        } else {
                            this.value = '';
                        }
                    });

        });
Efficient answered 11/11, 2013 at 20:17 Comment(0)
D
0

use autoFocus: true

$('#Animal').autocomplete({
    source: url,
    **autoFocus: true,**
    minlength: 1,
    select: function (event, ui) {
        $("#Animal").val(ui.item.value);
        changeUsersAnimal(ui.item.id);
    }
});
Dire answered 24/9, 2015 at 9:41 Comment(0)
T
0

Updated Scott Gonzalez' code to work with Jquery-UI 1.12

(function($) {

    $.ui.autocomplete.prototype.options.autoSelect = true;
    $('body').on('blur', '.ui-autocomplete-input', function(event) {
        var autocomplete = $(this).data('ui-autocomplete');
        if (!autocomplete.options.autoSelect || autocomplete.selectedItem) { return; }

        var matcher = new RegExp($.ui.autocomplete.escapeRegex($(this).val()), 'i');
        autocomplete.widget().children('.ui-menu-item').each(function(index, item) {
            var item = $( this ).data('uiAutocompleteItem');
            if (matcher.test(item.label || item.value || item)) {
                autocomplete.selectedItem = item;
                return false;
            }
        });

        if (autocomplete.selectedItem) {
            autocomplete
            ._trigger('select', event, {item: autocomplete.selectedItem});
        }
    });

}(jQuery));
Tinctorial answered 21/6, 2018 at 22:35 Comment(0)
B
0

I've tried all these responses and the final solution for me was put the select empty if the value introduced is not in the list:

            $( "#input" ).autocomplete({
                source: source,
                minLength: 0,
                change: function(event, ui) {
                    if(!ui.item) {
                        $(this).val("")
                    }
                }
            }).focus(function(){            
                $(this).data("uiAutocomplete").search($(this).val());
            });
Biisk answered 19/9, 2019 at 9:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.