I am using Jquery UI's autocomplete for multiple selectable values. All is good except the list of options closes after each selection. I would like the options to stay open until the user decides they are done making selections. I have looked through the documentation and I don't see any way to keep the options open. Any ideas?
<meta charset="utf-8">
<script>
$(function() {
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
function split( val ) {
return val.split( /,\s*/ );
}
function extractLast( term ) {
return split( term ).pop();
}
$( "#tags" ).autocomplete({
minLength: 0,
source: function( request, response ) {
// delegate back to autocomplete, but extract the last term
response( $.ui.autocomplete.filter(
availableTags, extractLast( request.term ) ) );
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
});
});
</script>
Tag programming languages:
You can do something like this:
first define a variable named readyToClose
and set it to be false
at the begining. And when you want to close your menu on next selection, set this variable to the true
. And also we should reimplement the close
method of JQuery UI.
Here I reimplemented the close
method of JQuery UI in your code, not in the source file! This is the same thing we do to render the list in custom way (e.g. http://jqueryui.com/demos/autocomplete/custom-data.html )
var readyToClose = false;
$( "#tags" ).autocomplete({
minLength: 0,
source: function( request, response ) {
// delegate back to autocomplete, but extract the last term
response( $.ui.autocomplete.filter(
availableTags, extractLast( request.term ) ) );
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
}).data( "autocomplete" ).close = function(e){
if(readyToClose)
clearTimeout(this.closing), this.menu.element.is(":visible") && (this.menu.element.hide(), this.menu.deactivate(), this._trigger("close", e));
else
return false;
};
Note: In newer versions of jQuery (i.e. 1.9.0), replace "autocomplete" with "uiAutocomplete", as in:
$("#tags")
.autocomplete({...})
.data("uiAutocomplete").close = ...
I know it's an old question that may no longer by relevant to the OP, but for the sake of completeness, a cleaner solution would be to extend
the autocomplete widget and overriding _close
, as well as extend
ing the event object in the select
event handler. This allows you to perform custom logic to determine whether the menu should be closed or not on a case by case (event by event) basis. See also http://learn.jquery.com/jquery-ui/widget-factory/extending-widgets/
//override the autocomplete widget
jQuery.widget( "ui.autocomplete", jQuery.ui.autocomplete, {
_close: function( event ) {
if(event!== undefined && event.keepOpen===true) {
//trigger new search with current value
this.search( null, event );
return true;
}
//otherwise invoke the original
return this._super( event );
}
});
$('ac').autocomplete(
{
...custom options...
select: function( event, ui ) {
...custom logic...
if(menu should remain open) {
//extend original event with special flag to keep dropdown open
//the o-event continues to be passed through the chain of listeners
//and will end up being processed during _close()
jQuery.extend(event.originalEvent,{keepOpen:true});
//modify value as required
jQuery(this).val(...);
return false; //prevent selected value from being set,
//i.e. keeping modified value above
}
}
}
);
In the above jQuery.extend(event.originalEvent,{keepOpen:true}); is used to add a special keepOpen
property to event.originalEvent
. Since extend
modifies the original object (first argument), any subsequent use of the same event.originalEvent
will have this property available. Having read and stepped through the code it ends up being the same object that is referenced by event
in the _close
method.
This code will break should this change in future, however, it is a lot simpler to maintain than the equivalent of what ingredient_15939 suggested 18 months ago.
The jQuery UI team thinks this is bad UX practice: http://forum.jquery.com/topic/enhanced-autocomplete-interest-in-getting-this-into-jqueryui#14737000001125152
So no built-in way to cancel it. You can try re-triggering the autocomplete after the select & close events. And probably you should cache the results array so that no new requests are made (if it's in Ajax mode).
Didn't get into the details though - I have convinced my UI designer that we don't need this for this version :)
I needed this same ability. IMO the authors could have easily given us the option. What I did was exclude autocomplete from the UI bundle, and include an edited copy of the separate file: jquery.ui.autocomplete.js
(get it from the "development-bundle\ui" folder).
I added a couple of new options, closeOnSelect
and updateElement
(both boolean default true), as follows (at the top of file):
$.widget("ui.autocomplete", {
options: {
...
closeOnSelect: true, // add this line - controls list closing.
updateElement: true // add this line - controls updating input box.
},
Then search for the string "selected:" in the code (there will be only one occurrence). Within this function, replace these last few lines:
if ( false !== self._trigger( "select", event, { item: item } ) ) {
self.element.val( item.value );
}
// reset the term after the select event
// this allows custom select handling to work properly
self.term = self.element.val();
self.close( event );
self.selectedItem = item;
With this:
if (false !== self._trigger("select", event, { item: item })) {
if (self.options.updateElement) {
self.element.val(item.value);
self.term = self.element.val(); // Ensure term string is same.
}
if (self.options.removeOnSelect) { // Remove menu item if option is true.
console.log(ui);
} else {
self.selectedItem = item;
}
if (self.options.closeOnSelect) self.close(event); // Close menu if option is true.
} else { // Returned false.
self.term = self.element.val(); // Ensure term string is same, in case callback changed element value.
}
Then search for string "focus: function" (again, only one occurrence). Within that function, replace the line:
self.element.val(item.value);
With this:
if (self.options.updateElement) self.element.val(item.value);
Now, when you create the autocomplete, simply set those two options as you like:
$("#txtsearch").autocomplete({
closeOnSelect: false, // Keep list open when item selected.
updateElement: false, // Don't change the input box contents.
// etc...
This works perfectly for me. Regardless of whether it's deemed "bad UI practice" by the original authors, everyone's needs are different and options should be there! :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With