I\'m using jQuery UI\'s Autocomplete slightly differently than it was probably created to do.
Basically I want to keep all the same functionality, the only differenc
Going with $input.data("autocomplete").menu.options.selected = function(){} caused value not holding after selecting different item (our implementation needed to append to the end. Possibly needed just to add e.preventDefault() or return false before append code). So I just made a switch in the close event. example with putting external variable and writing own method is better, but also did not like it. I first though of calling method manually with passing a parameter when need to close autocomplte by hand. (in our implementation customer required the list to be open when clicking the items, but close when mouse leaves the textbox container.
So I just attached autocomplete to the textbox`s element container and attached mouseenter and mouseleave. To determine if it should close, I used the jQuery(this).data("canClose") custom variable. Basically, what it does, is just reopening the autocomplete with search method when variable is 'false'.
Here is the final code:
element.autocomplete({
minLength:0,
source: source,
appendTo: element.parent(),
close: function () {
if (!jQuery(this).data("canClose")) {
jQuery(this).autocomplete('search', '');
}
return false;
}
});
element.mouseenter(function () {
element.data("canClose", false);
jQuery(this).autocomplete('search', '');
});
element.parent().mouseleave(function () {
element.data("canClose", true);
element.delay(2000).autocomplete("close");
});
if You need to do the append instead of replace value add the select handler in the constructor:
select: function (event, ui) {
var text = element.text().trim();
if (text.length > 0 && !text.endsWith(",")) {
text += ", ";
}
jQuery(this).text((text + ui.item.label));
jQuery(this).focus();
return false;
}
The second syntax using .bind()
is simply another way of attaching an event handler to jQueryUI's custom events. This is exactly the same as defining the event handler inside of the widget options (using select: function(event, ui) { }
)
Imagine if you had several autocomplete widgets on the page and you wanted to execute the same function when any of them raised the "select" event for example:
$(".autocomplete").bind("autocompleteselect", function(event, ui) {
/* Will occur when any element with an autocomplete widget fires the
* autocomplete select event.
*/
});
As for cancelling the select
event, you have that correct. However, cancelling the close
event is a little tougher; it looks like returning false from the event handler won't work (close
is fired after the menu is actually closed). You could perform a little hackery and just replace the select
function with your own:
var $input = $("input").autocomplete({
source: ['Hello', 'Goodbye', 'Foo', 'Bar']
});
$input.data("autocomplete").menu.options.selected = function(event, ui) {
var item = ui.item.data( "item.autocomplete" );
$input.focus();
};
Here's a working example of that: http://jsfiddle.net/ZGmyp/
I am not sure what the ramifications are of overriding the close event, but it doesn't look like anything crazy is happening in the simple example. I would say that this is kind of an unnatural use of the widget, so there may be unexpected consequences.
Taking inspiration from Andrews solution, I found a way to keep autocomplete open on selection with less impact on core functionality:
var selected; //flag indicating a selection has taken place
var $input = $("input").autocomplete({
source: ['Hello', 'Goodbye', 'Foo', 'Bar'],
select: function( event, ui ) {
selected = true;
}
});
//Override close method - see link below for details
(function(){
var originalCloseMethod = $input.data("autocomplete").close;
$input.data("autocomplete").close = function(event) {
if (!selected){
//close requested by someone else, let it pass
originalCloseMethod.apply( this, arguments );
}
selected = false;
};
})();
So the idea is to neuter close method when appropriate, as indicated by selected flag. Having selected flag in global name space probably isn't the best idea, but that's for someone else to improve on :-).
More about overriding methods
I went down a slightly different route for this and expanded on Andrew's fiddle
The purpose being that I always wanted the auto-complete to show whilst a certain input had focus - allowing for multiple selections.
$("#myInput").autocomplete({
source: ["Test", "This", "Doesnt", "Close"],
minLength: 0,
select: function (event, ui) {
// Add your own custom login to manipulate ui.item.label and add what you need the input field (and ui.item.value if required.)
// We've customised how we want the select to "work" so prevent the default
// of auto clearing the input.
event.preventDefault();
},
close : function(event)
{
// We're closing the autocomplete - check if the input still has focus...
if ($("#myInput").is(":focus"))
{
// Prevent the auto complete from closing.
event.preventDefault();
// Make sure we're reshowing the autcomplete - since the input would have momentarily
// lost focus when we selected an item.
$("#myInput").autocomplete("search", "")
}
}
});
$("#myInput").focus(function () {
// We're not taking any filtering into account for this example.
$(this).autocomplete("search", "")
});
I tried the various ideas others have presented here without success.
I am using Jquery 2.1.4 with UI 1.11.4 and this is how I got this to work:
Javascript:
<script>
var lookup_selectable = false;
var lookup_term = '';
$(function() {
$( "#lookup" ).autocomplete({
source: "lookup_processor.php",
minLength: 3,
renderItem: function( ul, item ) {
// This function is called for each item returned from the 'source:'
// It is up to you to ensure that a list item element is returned.
// do whatever logic on the item data to determine if it should not be slectable..
//Example:
// The backend "source" has handled the logic of what is selectable or not
// and has set a 'selectable' parameter that we can use
if(item.selectable){
// return the item unchanged from autocompletes default behavior
return $("<li></li>").data("item.autocomplete", item).append("<a>" + item.label + "</a>").appendTo(ul);
}else{
// this item is not selectable so lets apply a class named 'item-disabled' to give a visual queue.
// We are also wrapping the label in a span instead of an anchor just to show that the item is still clickable, darn!
return $('<li class="ui-menu-item item-disabled"></li>').data("item.autocomplete", item).append('<span>'+item.label+'</span>').appendTo(ul);
}
},
select: function( event, ui ) {
// This item was clicked ..
// save the item.clickable value to our own external variable
// Note: We have to do this because the item object is not available in the 'close' function :-(
lookup_selectable = ui.item.selectable; // the item object is available inside the ui parameter
// store the current search term
lookup_term = $('#lookup').val();
// do any additional stuff based on the item selected, if needed...
},
close: function(event, ui){
// This function fires after select: and after autocomplete has already "closed" everything. This is why event.preventDefault() won't work.
// ** ui is an empty object here so we have to use our own variable to check if the selected item is "selectable" or not..
if (! lookup_selectable){
// We need to undo what autocomplete has already done..
$('#lookup').val(lookup_term); // Restore the search term value
$('#'+event.currentTarget.id).show(); // Keep the selection window open
// ta-da! To the end user, nothing changes when clicking on an item that was not selectable.
}
}
});
});
</script>
CSS:
<style>
li.ui-menu-item.item-disabled {
text-decoration: none;
line-height: 1.5;
color: #ccc;
}
</style>
Backend Source "lookup_processor.php":
<?php
$search_results = array();
// ..do whatever to get the data for each item
$item_data = getting_item_data();
foreach ($item_data as $data){
// The id, label, and value keys are the typical keys that autocomplete expects, but you can add ass many others as you want..
// For our example we are setting the 'selectable' key to true or false based on some simple example logic
$search_results[] = array(
'id'=>$data['id'],
'label'=>$data['label'],
'value'=>$data['value'],
'selectable'=>$data['some_thing_to_check']>0?true:false, // This is the parameter our 'select:' function is looking for
'send_it_all_if_you_want'=>json_encode($data)); // this is just an example of how you can send back anything you want
);
}
// send the results back to autocomplete
echo json_encode($search_results);
exit;
?>