How to disable element in jQuery autocomplete list
Asked Answered
H

3

14

Is it possible to disable a list element in an autocomplete, so it is not chooseable (and greyed out)?

I have this code, taken from the jQuery UI example page:

HTML:

<input id="tags" />

jQuery:

$(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"
  ];
  $( "#tags" ).autocomplete({
    source: availableTags
  });
});

As an example - is it possible to disable the last element if there are more than 3 items in the list?

In my real code, I have an AJAX request but I do not want to show more than 20 results in the autocomplete box, so if there are more than this, it should show something like "please narrow down your search" and disable the last element, so it is not chooseable (but only the last element should be disabled).

The above code is here with a Fiddle demo, http://jsfiddle.net/m6zvf/

Hoax answered 27/8, 2013 at 6:25 Comment(0)
H
32

If I understand correctly you want to add a disabled option with the message saying to narrow down the search when the results are more than X, for that you'd need a custom response and render methods:

Working fiddle

$(function () {
    $("#tags").autocomplete({
        source: availableTags,
        response: function (event, ui) {
            //Check if we have more than 3 results
            if (ui.content.length > 3) {
                //Remove all elements until there are only 3 remaining (the use of slice() was not supported)
                while (ui.content.length > 3) {
                    ui.content.pop();
                }
                //Add message
                ui.content.push({
                    'label': 'Please narrow down your search',
                     'value': ''
                });
            }
        }
    }).data("ui-autocomplete")._renderItem = function (ul, item) {
        //Add the .ui-state-disabled class and don't wrap in <a> if value is empty
        if(item.value ==''){
            return $('<li class="ui-state-disabled">'+item.label+'</li>').appendTo(ul);
        }else{
            return $("<li>")
            .append("<a>" + item.label + "</a>")
            .appendTo(ul);
        }
    };
});

You can refer to the documentation for more info on the response method, the _renderItem function is not documented but I found it in the source code of one of the examples

Heeler answered 27/8, 2013 at 7:11 Comment(3)
This is almost working perfectly except when scrolling down the elements with the keyboard, it is possible to select the disabled element (it is not possible with the mouse). How to avoid selecting it with the keyboard too?Hoax
@JohnMalli See update, I fixed the issue with the keyboard, also I don't know if you noticed but there's a way to make the options scrollable, don't know if that's something you'd want instead of limiting the number of options displayed jqueryui.com/autocomplete/#maxheightHeeler
Any suggestion on what to do if you end up with no selectable items in the list? So the only item shown is one of the ui-state-disabled ones. JQuery UI in 1.12 throws a major hissy fit. In 1.9, it only prevents you from selecting anything newSetser
M
4

With some trick work you could do something around :

JS

$( "#tags" ).autocomplete({
      source: availableTags,
        focus:function(e){e.stopPropagation();return false;},
        select:function(e){e.stopPropagation();return false;}
    });

CSS

.ui-autocomplete .ui-state-focus{
    border:0 !important;
    background:0 !important;
}

http://jsfiddle.net/techunter/zyGNQ/

EDIT :

You need to modify the renderer then :

$( "#tags" ).autocomplete({
      source: availableTags,
        focus:function(e, ui){
            //if focusing on the extra elements return false thus doing nothing
            return ui.item.idx<=2;
        },
        select:function(e, ui){
            //if selecting on the extra elements return false thus doing nothing
            return ui.item.idx<=2;}
    }) .data( "ui-autocomplete" )._renderItem = function( ul, item ) {
        //small trick to get the current element index while the list is constructing, we set this as a helper for later usage inside the item.
        item.idx=ul[0].childElementCount;
           return $( "<li>" )
               //if index is greater than 2 then we add a custom 'disable' class. this will help formating the disabled elements
               .toggleClass('disable',ul[0].childElementCount>2)
               //appending the element
               .append( "<a>" + item.label + "</a>" ).appendTo( ul );
    };

EDIT 2, e.toElement trick

found this while looking into the event :

$("#tags").autocomplete({
        source: availableTags,
        focus: function (e, ui) {
            $(e.toElement).toggleClass('ui-state-focus', ui.item.idx <= 2);
            return ui.item.idx <= 2;
        },
        select: function (e, ui) {
            return ui.item.idx <= 2;
        }
    }).data("ui-autocomplete")._renderItem = function (ul, item) {
        item.idx = ul[0].childElementCount;
        return $("<li>").append("<a>" + item.label + "</a>").appendTo(ul);
    };

No more need of the CSS.

http://jsfiddle.net/techunter/zyGNQ/

Maricruzmaridel answered 27/8, 2013 at 6:47 Comment(4)
You have disabled all of them - how to disable only the last one, if more than 3 elements in the list?Hoax
TecHunter - to be honest I prefer @koala_dev code as I think it is more simple and easy to understand. Your code is not perfect with the disabled element (it shakes a little when the mouse hovers it) and it is not clear to me how it actually works. If koala_dev gets the last part correct (with the keyboard) I will credit him for the solution. But thanks anyway :-)Hoax
I don't care for the removing part - I care for the code being easy to understand in a half year or alike and koala_dev's code I fully understand. Your code is "doing something weird" with the disabled element, but it is much cleaner to use the standard way, "ui-state-disabled". But I don't know if the standard way is preventing it from working correctly with the keyboard too.Hoax
You can still select the items with the arrow keys.Huehuebner
X
1

Arrow keys should be resolved too. Disabled items should be skipped. Disabled items are marked by attribute in source. It can be done by changing of active item nad setting corresponding class:

    $( "#tags" ).autocomplete({
    source: [
        {value: "ActionScript", disabled: false},
        {value: "AppleScript", disabled: true},
        {value: "Asp", disabled: false},
        {value: "BASIC", disabled: true},
        {value: "Erlang", disabled: false},
        {value: "Fortran", disabled: true}
    ],
    create: function () {
        $(this).data('ui-autocomplete')._renderItem = function (ul, item) {
            let value = item.value;
            let listItem;
            if (item.disabled) {
                listItem = $('<li class="ui-state-disabled"><div>' + value + '</div></li>')
                    .appendTo(ul);
            } else {
                listItem = $("<li>")
                    .append('<div>' + value + '</div>')
                    .appendTo(ul);
            }
            return listItem;
        }
    },
    select: function (event, ui) {
        if (ui.item.disabled) {
            //  suppress disabled items
            event.preventDefault();
        }
        return false;
    },
    focus: function (event, ui) {
        if (ui.item.disabled) {
            //
            //  skip disabled items
            //
            let data = $(this).data('ui-autocomplete');
            $(data.menu.active).find('div.ui-state-active').removeClass('ui-state-active'); // remove active class

            if (event.originalEvent.key === 'ArrowDown') {
                let liBefore = $(data.menu.active).prev(); // li before key pressing
                let nextLi = data.menu.active;
                if (!$(nextLi).is(':last-child')) {
                    while ($(nextLi).hasClass('ui-state-disabled')) {
                        // search first not disabled item
                        nextLi = $(nextLi).next();
                    }
                    if (nextLi.length === 0) {
                        // not found
                        nextLi = liBefore;
                    }
                } else {
                    // last item
                    nextLi = liBefore;
                }

                // setting of active item in jquery-ui autocomplete
                data.menu.active = nextLi;
                $(nextLi).find('div').addClass('ui-state-active');
            } else {
                if (event.originalEvent.key === 'ArrowUp') {
                    let liBefore = $(data.menu.active).next(); // li before key pressing
                    let prevLi = data.menu.active;
                    if (!$(prevLi).is(':first-child')) {
                        while ($(prevLi).hasClass('ui-state-disabled')) {
                            // search first not disabled item
                            prevLi = $(prevLi).prev();
                        }
                        if (prevLi.length === 0) {
                            // not found
                            prevLi = liBefore;
                        }
                    } else {
                        // first item
                        prevLi = liBefore;
                    }

                    // setting of active item in jquery-ui autocomplete
                    data.menu.active = prevLi;
                    $(prevLi).find('div').addClass('ui-state-active');
                }
            }
        }

        return false;
    }
});
Xanthochroism answered 30/11, 2020 at 15:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.