jQuery UI Autocomplete disable Select & Close events
Asked Answered
R

5

17

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 difference is that when the suggestion box appears, I don't the suggestion box to hide when a user makes a selection and I also don't want that selection to populate the input box that .autocomplete is attached to.

So, I've been reading through the jQuery UI documentation, and it appears there is a way to disable the Select: and Close: events, but I find the way they have explained it to be very confusing and hence, this is why I'm here asking for help.

My jQuery

$( "#comment" ).autocomplete({
    source: "comments.php",
    minLength: 4,

    // Attempt to remove click/select functionality - may be a better way to do this        
    select: function( event, ui ) {
        return false;
    },
    // Attempt to add custom Class to the open Suggestion box - may be a better way
    open : function (event, ui) {
        $(this).addClass("suggestion-box");
    },
    // Attempt to cancel the Close event, so when someone makes a selection, the box does not close
    close : function (event, ui) {
        return false;   
    }
});

Official jQuery UI documentation

Triggered when an item is selected from the menu; ui.item refers to the selected item. The default action of select is to replace the text field's value with the value of the selected item. Canceling this event prevents the value from being updated, but does not prevent the menu from closing.

Code examples

Supply a callback function to handle the select event as an init option.
$( ".selector" ).autocomplete({
   select: function(event, ui) { ... }
});
Bind to the select event by type: autocompleteselect.
$( ".selector" ).bind( "autocompleteselect", function(event, ui) {
  ...
});

Confusion

What confuses me is that they seem to be suggesting to remove the .autocomplete and replace with .bind("autocompleteselect") - which will disable the autocomplete altogether?

Thank you very much for any help you can give.

Radke answered 18/5, 2011 at 10:52 Comment(0)
K
12

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.

Kiddush answered 18/5, 2011 at 12:28 Comment(7)
Thanks very much for that @Andrew, can you think of a way to cancel the Click / select function, but still change the suggestion box when the input is changed and/or when the user deselects the input box?Radke
@thathurtabit: for the menu should still update as you type suggestions. What do you mean by changing the suggestion box when the input is deselected though?Kiddush
@AndrewWhitaker Thanks for getting back to me, yes, I'm looking for the menu to still update when suggestions are typed in. I've just tried your demo again, and it does update if you type in another suggestion it recognises, but if you type in someone completely different (and it doesn't recognise) it still keeps its previous irrelevant suggestion. Also, when you click outside of the input box (to get rid of its focus) the suggestion box remains open.Radke
@thathurtabit: I understand--I'll take another lookKiddush
@thathurtabit: This might take a little longer to figure out than I originally thought. I'll continue looking into it though!Kiddush
@thathurtabit: Try out the most recent code in my answer. It's starting to feel more hacky, but should work.Kiddush
@Andrew Whitaker, that seems to work perfectly! You're a legend! Thanks very much indeed for your help, sir.Radke
L
21

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

Lineman answered 2/7, 2011 at 13:44 Comment(4)
Great idea to override the built-in methods. Thanks.Monitorial
+1, your solution helped. And it's quite often helpful to patch up the methods of jQuery UI (and similar widgets) this way.Headcloth
Works fine. But in 2019 (for actual jQuery versions) you need to use $input.data("uiAutocomplete") instead of $input.data("autocomplete") to make it work.Marcellemarcellina
or .data("ui-autocomplete")Ganister
K
12

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.

Kiddush answered 18/5, 2011 at 12:28 Comment(7)
Thanks very much for that @Andrew, can you think of a way to cancel the Click / select function, but still change the suggestion box when the input is changed and/or when the user deselects the input box?Radke
@thathurtabit: for the menu should still update as you type suggestions. What do you mean by changing the suggestion box when the input is deselected though?Kiddush
@AndrewWhitaker Thanks for getting back to me, yes, I'm looking for the menu to still update when suggestions are typed in. I've just tried your demo again, and it does update if you type in another suggestion it recognises, but if you type in someone completely different (and it doesn't recognise) it still keeps its previous irrelevant suggestion. Also, when you click outside of the input box (to get rid of its focus) the suggestion box remains open.Radke
@thathurtabit: I understand--I'll take another lookKiddush
@thathurtabit: This might take a little longer to figure out than I originally thought. I'll continue looking into it though!Kiddush
@thathurtabit: Try out the most recent code in my answer. It's starting to feel more hacky, but should work.Kiddush
@Andrew Whitaker, that seems to work perfectly! You're a legend! Thanks very much indeed for your help, sir.Radke
R
4

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;

?>
Radioman answered 22/3, 2016 at 17:44 Comment(0)
O
1

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", "")
});
Oily answered 14/4, 2015 at 11:43 Comment(1)
This really helped me create a dynamic multi level selection, what I needed was if you select certain item, you get a new list of items. Spent hours trying to figure this out. Everyone does the simple options but dont demo this feature. will write a page for this feature very soon.Hudnut
P
0

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;
                }
Pickpocket answered 5/12, 2016 at 11:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.