22

I am using jQuery UI Selectable plugin. I want to select one item at a time. But jQuery UI Selectable plugin allows multiple selection by clicking/ dragging / holding CTRL key. Is there any way to prevent multiple selection?

miti737
  • 471
  • 2
  • 7
  • 18

10 Answers10

29

What i did, is that i allow multiple selection but when the selection is done, i only keep the first element selected

<ul id="select">
    <li>Row 1</li>
    <li>Row 2</li>
    <li>Row 3</li>
</ul>

This selects all the selected elements except the first one and deletes the selected status. So at the end, only one element will be selected. event.target corresponds to my ul element.

$('#select').selectable({
    stop:function(event, ui){
        $(event.target).children('.ui-selected').not(':first').removeClass('ui-selected');
    }
});

I know this topic is kinda old, but i still stumbled upon it, so it can be useful to someone else

Lunfel
  • 2,499
  • 21
  • 29
  • 4
    Old or not, you pointed me in the right direction. Thanks dood. – iLLin Jan 08 '12 at 16:06
  • Like you I just came across this question. I wrote a solution at [http://stackoverflow.com/a/13727528/1747491](http://stackoverflow.com/a/13727528/1747491) that I feel is cleaner. This method causes the `.ui-selecting` style to be very noticeable. Also, when trying to select down the list you have to deselect the currently selected item (not so for up). – theblang Dec 05 '12 at 16:30
15

This worked for me. It prevents "lassoing" multiple rows and ctrl + click.

$(function() {
$("#selectable").selectable({
      selecting: function(event, ui){
            if( $(".ui-selected, .ui-selecting").length > 1){
                  $(ui.selecting).removeClass("ui-selecting");
            }
      }
});
});
kyle
  • 568
  • 2
  • 10
  • 26
  • Worked like a charm! Just better to change `$(".ui-selected, .ui-selecting").length` to `this.element.find(".ui-selected, .ui-selecting").length`. – deerchao Jul 12 '13 at 09:25
  • 1
    I'm not sure I understand the advice to switch. Here's the `$(".ui-selected, .ui-selecting").length` [version that seems to work](http://jsfiddle.net/ftL8w/). Here's the `this.element.find(".ui-selected, .ui-selecting").length` [version that seems broken](http://jsfiddle.net/xTr6F/1/), which throws the following error: `Error: Unable to get value of the property 'find': object is null` I'm pretty new to jQuery, and I'm just trying to understand. – twip Nov 25 '13 at 22:09
10

There is no defined way. However, you could pass in a callback function for "Start" or "Selected" events, that cancels the selection if more than one element is selected.

Dmitri Farkov
  • 9,133
  • 1
  • 29
  • 45
9

This might be a better solution:

$('#selectable').selectable({
    selecting: function (event, ui) {
        $(event.target).children('.ui-selecting').not(':first').removeClass('ui-selecting');
    }
});
Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
samit basak
  • 91
  • 1
  • 1
7

Here's a more general solution than those previously posted:

    $element.selectable({
        selecting: function (e, ui) {
            // force single selection
            $(e.target).find('.ui-selectee.ui-selecting').not(ui.selecting).removeClass('ui-selecting');
            $(e.target).find('.ui-selectee.ui-selected').not(ui.selecting).removeClass('ui-selected');
        }
    });

(The selectees may not always be children of the selectable, and holding onto the "first" selectee causes some weird behavior when you ctrl+click.)

Josh Schultz
  • 8,000
  • 9
  • 32
  • 39
6

yes, you can prevent this behavior, just set the toletance option to 'fit'

KenMaster
  • 69
  • 1
  • 1
  • This solves part of the problem - still need a way to prevent the user from holding the Ctrl key and selecting elements with a left mouse button click – Adam Apr 17 '12 at 16:55
  • all this means is that you have to lasso the whole row rather than just any part of the row – Dave Cousineau Nov 22 '17 at 17:47
2

An interesting discussion about this you can find on this jQuery forum thread.

cetnar
  • 9,357
  • 2
  • 38
  • 45
2

You can create your custom plugin like this:

$.widget("xim.singleSelectable", {
    options: {
        select: null
    },
    _create: function () {
        var self = this;
        this.element.addClass('ui-selectable');
        this.element.delegate('li', 'click', function (e) {
            self.element.find('>li').removeClass('ui-selected');
            $(this).addClass('ui-selected');

            if ($.isFunction(self.options.select)) {
                self.options.select.apply(self.element, [e, this]);
            }

        });
    },
    selected: function () {
        return this.element.find('li.ui-selected');
    },
    destroy: function () {
        $.Widget.prototype.destroy.apply(this, arguments); // default destroy
    }
});
Guillermo
  • 419
  • 4
  • 5
0

There are some good solutions here, but most of them assume that you always want to select the first element as it appears in the DOM in a multiple selection case.

To remedy this, I keep a variable (lastSelection) that contains the last element that was requested successfully (rather than the first in the DOM) to fallback to in the case of an unwanted selection.

var lastSelection;// this will record our last successful selection

$('#selectable').selectable({
    filter: '.element',
    selecting: function () {
        if ($('.ui-selecting').length > 1) {
            // if selecting multiple (lasso) we will ignore the selection and fallback
            $('.ui-selecting').removeClass('ui-selecting');
            $(lastSelection).addClass('ui-selecting');// if no value, nothing will be selected
        }     
    },
    stop: function () {
        if ($('.ui-selected').length > 1) {
            // looks like we have an invalid selection, fallback to lastSelection
            // this handles the ctrl (windows), cmd (OSX) multi-select cases
            $('.ui-selected').removeClass('ui-selected');
            $(lastSelection).addClass('ui-selected');// if no value, nothing will be selected
        } else {
            if ($('.ui-selected').first().is('.element')) {
                // if we successfully found a selection, set it as our new lastSelection value
                lastSelection = $('.ui-selected')[0];
            }     
        }     
    }
});

Note: If you want to deselect without ctrl / cmd + click you will have to implement a work-around with this method.

I also wanted to point out that tolerance: 'fit' simply requires that the lasso completely surrounds a target element in order to select it, it will not disable the lasso selection unless your elements cannot be surrounded in the available lasso area. http://api.jqueryui.com/selectable/#option-tolerance

bandy
  • 1
  • 1
0

If you want to disable non consecutive multiselection but still wish to keep the dragging selection you can do this.

         stop: function( event, ui ) {
                if( $(".ui-selected, .ui-selecting").length > 1){
                    var elems = $('.ui-selected, .ui-selecting');

                    for(var i = 0; i < elems.length - 1; i++){
                        if($(elems[i]).closest('td').next('td').text() != $(elems[i+1]).text()){
                           //Non consecutive selection detected
                        }
                    }
                }
            }

It essentially checks if all elements are next to each other.

Cellydy
  • 1,365
  • 3
  • 15
  • 27