44

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.

felipe.zkn
  • 1,804
  • 7
  • 28
  • 62
stephen776
  • 8,546
  • 13
  • 66
  • 118

9 Answers9

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.

Jason Gray
  • 739
  • 1
  • 6
  • 13
  • 16
    When you don't use ids but only labels you may want to use this instead: (inside change function) `if (!ui.item) { $(this).val(''); }` – collimarco Sep 25 '12 at 11:16
  • 2
    When you select an item via pressing "Enter" key - ui will be always null. jQuery UI v1.11.0 – asologor Aug 01 '14 at 14:24
  • 1
    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. – BornToCode Aug 06 '15 at 13:08
  • @collimarco both the answer and your comment worked like a charm. I really work much better with the tab and enter keys. – ArabianMaiden Apr 23 '21 at 11:59
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); 
        }
    }
}
Alessio Cantarella
  • 4,659
  • 3
  • 21
  • 29
Victor
  • 4,635
  • 1
  • 23
  • 30
  • 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: http://jsfiddle.net/jlowery2663/o4n29wn3/ – Jeff Lowery Nov 18 '14 at 21:20
  • 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? – B2K Aug 03 '15 at 15:11
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;
    }
});
Sam Battat
  • 5,647
  • 1
  • 18
  • 28
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;
  }
 }
});
Matanya
  • 5,911
  • 7
  • 43
  • 75
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

Amol Shiledar
  • 327
  • 3
  • 12
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.');
            }
        });
    }
});
DiskJunky
  • 4,320
  • 3
  • 30
  • 60
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. . . . . .

pomix
  • 1
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("");
         }
      }
   }
 });
  • 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. – muka.gergely Oct 24 '19 at 13:30
  • While this code snippet may be the solution, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-‌​code-based-answers) 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. – nircraft Oct 24 '19 at 14:28
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(''); 
}