Enable Shift-Multiselect in jQuery UI Selectable
Asked Answered
V

3

24

I want to enable multiselect capabilities in a jQuery UI Selectable table by holding shift.

I probably should do something like this if shift is held down on mouseclick

  • Get topmost selected element
  • Get clicked element
  • Select all elements in between

but i can't find how to do this in a clean way...

At the moment i got this inside the selectable configuration:

start: function(e)
        {
            var oTarget = jQuery(e.target);
            if(!oTarget.is('tr')) oTarget = oTarget.parents('tr');
        }

So oTarget is the clicked element (and e.currentTarget is the whole table) but now what? How can i find which elements are already selected in a way that can tell me if the clicked element is over or below the selected ones and select everything in between?

I've solved it now like this, added to the selectable element:

jQuery(table).mousedown(function(e)
    {
        //Enable multiselect with shift key
        if(e.shiftKey)
        {
            var oTarget = jQuery(e.target);
            if(!oTarget.is('.ui-selectee')) oTarget = oTarget.parents('.ui-selectee');

            var iNew = jQuery(e.currentTarget).find('.ui-selectee').index(oTarget);
            var iCurrent = jQuery(e.currentTarget).find('.ui-selectee').index(jQuery(e.currentTarget).find('.ui-selected'));

            if (iCurrent < iNew) {
                iHold = iNew;
                iNew = iCurrent;
                iCurrent = iHold;
            }

            if(iNew != '-1')
            {
                jQuery(e.currentTarget).find('.ui-selected').removeClass('ui-selected');
                for (i=iNew;i<=iCurrent;i++) {
                    jQuery(e.currentTarget).find('.ui-selectee').eq(i).addClass('ui-selected');
                }
                e.stopImmediatePropagation();
                e.stopPropagation();
                e.preventDefault();
                return false;
            }
        }
    }).selectable(...)
Vegetable answered 21/2, 2012 at 9:7 Comment(0)
B
13

I wrote simple plugin for that functionality. It's not dependent on jQuery ui Selectable plugin and as far as i know works fine with it.

You can find plugin code and simple example here: http://jsfiddle.net/bMgpc/170/

Going to write simple description below.

Basic usage:

$('ul').multiSelect();

If you hold "Ctrl" or "Command Key" then you can select/unselect elements one by one.

ul - parent that holds inner elements to be selected.

There are number of options available:

  • keepSelection - true|false - quite an important flag. If set to true (default), then selection won't be cleared if you click on already selected element (as it works in with multiple prop)
  • multiselect - true|false -if false you can select only one element
  • selected - 'selected' - class that will be added to selected element
  • filter: - ' > *' - what elements are we going to select
  • unselectOn - false|'selector' - if set then if clicked on set selector selectio would be removed
  • start: false|function - callback on start
  • stop: false|function - callback on stop
  • unselecting: false|function - callback when clicked on set "unselectOn" option

It's a dev version plugin, so use with care

Bauske answered 21/2, 2012 at 9:30 Comment(2)
Thanks, i didn't use this directly but i could get the needed ideas for working it out from the source. :)Vegetable
@bardiir, that's the best way. When i was writing this code i had very specific thing in mind, so rewriting it for your own purpose is the best way to go.Bauske
M
30

You can do it without plugins like this:

var prev = -1; // here we will store index of previous selection
$('tbody').selectable({
    selecting: function(e, ui) { // on select
        var curr = $(ui.selecting.tagName, e.target).index(ui.selecting); // get selecting item index
        if(e.shiftKey && prev > -1) { // if shift key was pressed and there is previous - select them all
            $(ui.selecting.tagName, e.target).slice(Math.min(prev, curr), 1 + Math.max(prev, curr)).addClass('ui-selected');
            prev = -1; // and reset prev
        } else {
            prev = curr; // othervise just save prev
        }
    }
});

Here is live demo: http://jsfiddle.net/mac2000/DJFaL/1/embedded/result/

Merrymaking answered 22/1, 2013 at 22:39 Comment(5)
This is a very clean and basic implementation, I love it. Expanding it for multi-multi-selects (using the shift-click part multiple times) is also easily accomplished with a few extra checks. Thank you!Prejudicial
This exhibits some odd behavior if you ever "drag" the first selection in the table. Dragging causes the event to occur on the td rather than tr so the index changes to a much higher value.Rosenwald
The selectable event fires for both td and tr so you can return on ui.selected.tagName === 'TD'Rosenwald
Hi! I have stumbled upon this and i have found that by setting prev to 0 and not resetting it (removing the prev = -1; line) will make it more "filesystem-like" that is it will allow repeated multiselect, will unselect elements if you shift-select towards them and will select from the start when no element is selected. I can't see any drawback to this so maybe it might be of interest to someoneBucket
Great solution and integrating what @Bucket suggested make it even better!Lunkhead
B
13

I wrote simple plugin for that functionality. It's not dependent on jQuery ui Selectable plugin and as far as i know works fine with it.

You can find plugin code and simple example here: http://jsfiddle.net/bMgpc/170/

Going to write simple description below.

Basic usage:

$('ul').multiSelect();

If you hold "Ctrl" or "Command Key" then you can select/unselect elements one by one.

ul - parent that holds inner elements to be selected.

There are number of options available:

  • keepSelection - true|false - quite an important flag. If set to true (default), then selection won't be cleared if you click on already selected element (as it works in with multiple prop)
  • multiselect - true|false -if false you can select only one element
  • selected - 'selected' - class that will be added to selected element
  • filter: - ' > *' - what elements are we going to select
  • unselectOn - false|'selector' - if set then if clicked on set selector selectio would be removed
  • start: false|function - callback on start
  • stop: false|function - callback on stop
  • unselecting: false|function - callback when clicked on set "unselectOn" option

It's a dev version plugin, so use with care

Bauske answered 21/2, 2012 at 9:30 Comment(2)
Thanks, i didn't use this directly but i could get the needed ideas for working it out from the source. :)Vegetable
@bardiir, that's the best way. When i was writing this code i had very specific thing in mind, so rewriting it for your own purpose is the best way to go.Bauske
M
5

After looking around I was unable to find a solution to this problem while still using jQuery UI's Selectable function, so I wrote one up. Essentially it taps into the selected / unselected callbacks of Selectable to manage DOM state while still honoring the callbacks as per the standard Selectable API. It supports the following use case:

  • Click one of the elements (shift+click, cntl+click, or click+drag also) anywhere in the list
  • Shift+click another element in the list
  • All elements between plus the two end points become selected

Usage for a table:

$('table').shiftSelectable({filter: 'tr'});

A few notes. (1) It currently only supports sibling elements. (2) It will pass through configuration options as you will see in the table example as well as the Selectable methods. (3) I heart underscore.js so it is used even though for this it is not essential. Feel free to swap out its simple checks and extends if you don't want to use this awesome library. And no, I have no affiliation with underscore.js. :)

table fiddle example

list fiddle example

Hope this helps someone else! Cheers.

Matteo answered 19/9, 2012 at 1:23 Comment(1)
This worked great for me. The only thing missing is the 'drag selection range' box which does show up on jquery-ui#selectable. But I'm sure there's an easy fix for that. Thank you!Loveliesbleeding

© 2022 - 2024 — McMap. All rights reserved.