jQuery UI AutoComplete: Only allow selected valued from suggested list
Asked Answered
F

9

45

I am implementing jQuery UI Autocomplete and am wondering if there is any way to only allow a selection from the suggested results that are returned as opposed to allowing any value to be input into the text box.

I am using this for a tagging system much like the one used on this site, so I only want to allow users to select tags from a pre-populated list returned to the autocomplete plugin.

Fusible answered 10/2, 2011 at 0:22 Comment(1)
See also: https://mcmap.net/q/363862/-autocomplete-disallow-free-text-entrySchnook
M
15

I got the same problem with selected not being defined. Got a work-around for it and added the toLowerCase function, just to be safe.

$('#' + specificInput).autocomplete({ 
  create: function () {
    $(this).data('ui-autocomplete')._renderItem = function (ul, item) {
      $(ul).addClass('for_' + specificInput); //usefull for multiple autocomplete fields
      return $('<li data-id = "' + item.id + '">' + item.value + '</li>').appendTo(ul); 
    };
  }, 
  change:
    function( event, ui ){
      var selfInput = $(this); //stores the input field
      if ( !ui.item ) { 
        var writtenItem = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val().toLowerCase()) + "$", "i"), valid = false;

        $('ul.for_' + specificInput).children("li").each(function() {
          if($(this).text().toLowerCase().match(writtenItem)) {
            this.selected = valid = true;
            selfInput.val($(this).text()); // shows the item's name from the autocomplete
            selfInput.next('span').text('(Existing)');
            selfInput.data('id', $(this).data('id'));
            return false;
          }
        });

        if (!valid) { 
          selfInput.next('span').text('(New)');
          selfInput.data('id', -1); 
        }
    }
}
Meteoritics answered 10/2, 2011 at 0:25 Comment(2)
I don't know why the linked-to example makes things so complicated. This version doesn't require an extra control and is pretty easy to understand: jsfiddle.net/jlowery2663/o4n29wn3Icicle
This is not working for me. select is undefined, and I suspect that input is as well. Do you have a version of this that has actually been tested?Claudianus
P
51

You could also use this:

change: function(event,ui){
  $(this).val((ui.item ? ui.item.id : ""));
}

The only drawback I've seen to this is that even if the user enters the full value of an acceptable item, when they move focus from the textfield it will delete the value and they'll have to do it again. The only way they'd be able to enter a value is by selecting it from the list.

Don't know if that matters to you or not.

Phthisis answered 2/8, 2012 at 15:27 Comment(4)
When you don't use ids but only labels you may want to use this instead: (inside change function) if (!ui.item) { $(this).val(''); }Demurrage
When you select an item via pressing "Enter" key - ui will be always null. jQuery UI v1.11.0Vita
This solution worked for me without the side effect you mentioned (of value being deleted when moving focus from the text field). Checked with jQuery autocomplete v1.8.Kennet
@Demurrage both the answer and your comment worked like a charm. I really work much better with the tab and enter keys.Beheld
M
15

I got the same problem with selected not being defined. Got a work-around for it and added the toLowerCase function, just to be safe.

$('#' + specificInput).autocomplete({ 
  create: function () {
    $(this).data('ui-autocomplete')._renderItem = function (ul, item) {
      $(ul).addClass('for_' + specificInput); //usefull for multiple autocomplete fields
      return $('<li data-id = "' + item.id + '">' + item.value + '</li>').appendTo(ul); 
    };
  }, 
  change:
    function( event, ui ){
      var selfInput = $(this); //stores the input field
      if ( !ui.item ) { 
        var writtenItem = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val().toLowerCase()) + "$", "i"), valid = false;

        $('ul.for_' + specificInput).children("li").each(function() {
          if($(this).text().toLowerCase().match(writtenItem)) {
            this.selected = valid = true;
            selfInput.val($(this).text()); // shows the item's name from the autocomplete
            selfInput.next('span').text('(Existing)');
            selfInput.data('id', $(this).data('id'));
            return false;
          }
        });

        if (!valid) { 
          selfInput.next('span').text('(New)');
          selfInput.data('id', -1); 
        }
    }
}
Meteoritics answered 10/2, 2011 at 0:25 Comment(2)
I don't know why the linked-to example makes things so complicated. This version doesn't require an extra control and is pretty easy to understand: jsfiddle.net/jlowery2663/o4n29wn3Icicle
This is not working for me. select is undefined, and I suspect that input is as well. Do you have a version of this that has actually been tested?Claudianus
C
6

http://jsfiddle.net/pxfunc/j3AN7/

var validOptions = ["Bold", "Normal", "Default", "100", "200"]
previousValue = "";

$('#ac').autocomplete({
    autoFocus: true,
    source: validOptions
}).keyup(function() {
    var isValid = false;
    for (i in validOptions) {
        if (validOptions[i].toLowerCase().match(this.value.toLowerCase())) {
            isValid = true;
        }
    }
    if (!isValid) {
        this.value = previousValue
    } else {
        previousValue = this.value;
    }
});
Cottontail answered 2/3, 2013 at 22:21 Comment(2)
is it valid usage for form when in form their have lots of jquery functions ?Educable
This doesn't work if you enter substring of any of the allowed items.Broadminded
S
6

This is how I did it with a list of settlements:

 $("#settlement").autocomplete({
  source:settlements,
  change: function( event, ui ) {
  val = $(this).val();
  exists = $.inArray(val,settlements);
  if (exists<0) {
    $(this).val("");
    return false;
  }
 }
});
Sibship answered 2/4, 2013 at 7:28 Comment(0)
B
6

i just modify to code in my case & it's working

selectFirst: true,
change: function (event, ui) {
        if (ui.item == null){ 
         //here is null if entered value is not match in suggestion list
            $(this).val((ui.item ? ui.item.id : ""));
        }
    }

you can try

Bartie answered 23/9, 2015 at 15:9 Comment(1)
Trying this but all values showing null. Cannot select anythingCist
A
1

Ajax submission and handling

This will be of use to some of you out there:

$('#INPUT_ID').autocomplete({
    source: function (request, response) {
        $.ajax({
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            url: autocompleteURL,
            data: "{'data':'" + $('INPUT_ID').val() + "'}",
            dataType: 'json',
            success: function (data) {
                response(data.d);
            },
            error: function (data) {
                console.log('No match.')
            }
        });
    },
    change: function (event, ui) {
        var opt = $(this).val();

        $.ajax({
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            url: autocompleteURL,
            data: "{'empName':'" + name + "'}",
            dataType: 'json',
            success: function (data) {
                if (data.d.length == 0) {
                    $('#INPUT_ID').val('');
                    alert('Option must be selected from the list.');
                } else if (data.d[0] != opt) {
                    $('#INPUT_ID').val('');
                    alert('Option must be selected from the list.');
                }
            },
            error: function (data) {
                $(this).val('');
                console.log('Error retrieving options.');
            }
        });
    }
});
Allhallows answered 16/3, 2018 at 19:24 Comment(0)
M
0

I'm on drupal 7.38 and to only allow input from select-box in autocomplete you only need to delete the user-input at the point, where js does not need it any more - which is the case, as soon as the search-results arrive in the suggestion-popup right there you can savely set:

 **this.input.value = ''**

see below in the extract from autocomplete.js ... So I copied the whole Drupal.jsAC.prototype.found object into my custom module and added it to the desired form with

 $form['#attached']['js'][] = array(
  'type' => 'file',
  'data' => 'sites/all/modules/<modulname>_autocomplete.js',
 );

And here's the extract from drupal's original misc/autocomplete.js modified by that single line...

Drupal.jsAC.prototype.found = function (matches) {
  // If no value in the textfield, do not show the popup.
  if (!this.input.value.length) {
    return false;
  }
  // === just added one single line below ===
  this.input.value = '';

  // Prepare matches.

=cut. . . . . .

Masquer answered 25/1, 2017 at 14:54 Comment(0)
D
0

If you would like to restrict the user to picking a recommendation from the autocomplete list, try defining the close function like this. The close function is called when the results drop down closes, if the user selected from the list, then event.currentTarget is defined, if not, then the results drop down closed without the user selecting an option. If they do not select an option, then I reset the input to blank.

//
// Extend Autocomplete
//
$.widget( "ui.autocomplete", $.ui.autocomplete, {      
   options: {
      close: function( event, ui ) {
         if (typeof event.currentTarget == 'undefined') {
            $(this).val("");
         }
      }
   }
 });
Dumbwaiter answered 23/10, 2019 at 18:57 Comment(2)
This code sample might be answering a question, but it's not clear that it does. Please elaborate your answer so it's easier to understand what it does and why.Bratcher
While this code snippet may be the solution, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.Formally
C
0

You can actually use the response event in combination to the change event to store the suggested items like so:

response: function (event, ui) {
    var list = ui.content.map(o => o.value.toLowerCase());
},
change: function (event, ui) {
    if (!ui.item && list.indexOf($(this).val().toLowerCase()) === -1 ) { $(this).val(''); 
}
Curitiba answered 2/10, 2020 at 2:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.