Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shift list element order in several <ul> at once if one of <li> changes position

I am using drag and drop functionality to allow users to order elements on a page. I have several <ul> elements with <li> elements inside of them (all <ul> contain 3 <li> elements) where each unordered list corresponds to a month, so

<ul class="dragable" id="month-june">
 <li>Item 1</li>
 <li>Item 2</li>
 <li>Item 3</li>
</ul>

<ul class="dragable" id="month-july">
 <li>Item 1</li>
 <li>Item 2</li>
 <li>Item 3</li>
</ul>

<ul class="dragable" id="month-august">
 <li>Item 1</li>
 <li>Item 2</li>
 <li>Item 3</li>
</ul>

<!-- etc.. -->

I want to somehow sort all of the lists once .drop() event occurs (basically users drop item in place after dragging it). I'm changing list positions in dom so they are always ordered there, for example if Item 3 from august is moved between item 1 and item 2 in july it will look like this:

<ul class="dragable" id="month-july">
 <li>Item 1</li>
 <li>Item 3</li>
 <li>Item 2</li>
 <li>Item 3</li>
</ul>

Now I need to figure out how to push Item 3 from july down to augusts unordered list and also push down all other items after it. This should have vice versa effects if for example Item 1 from june is draged into july between item 2 and item 3, in this case everything above it should shift left. Therefore I need to have 3 items in all list at any given time.

Here is image further showing it, that hopefully explains it better: (Consider middle section as initial and than arrows show where item is dragged and what happens to lists before and after it depending on position)

enter image description here

Could this be done without using ids or classes, but relying on next and previous elements (unordered lists), as I don't know exactly what months follow what in this case.

Here is very simple js fiddle with drag and drop behaviour: DEMO

like image 999
Ilja Avatar asked Nov 10 '22 10:11

Ilja


1 Answers

Updates on Drop

$(".sortable-queue").sortable({
    revert: true,
    scroll: false,
    connectWith: ".connected-sortable",
    placeholder: "item-dragable-placeholder",
    receive: function (event, ui) {
        var target = $(event.target).parent().parent();
        var source = ui.sender.parent().parent();
        var children = target.parent().children();

        var start = children.index(source);
        var end = children.index(target);

        // bubble up?
        if (start < end) {
            for (var i = start; i < end; i++) {
                $(children[i]).find("ul").append($(children[i + 1]).find("li").first().detach())
            }
        }
        // bubble down?
        else if (start > end) {
            for (var i = start; i > end; i--) {
                $(children[i]).find("ul").prepend($(children[i - 1]).find("li").last().detach())
            }
        }
        // same pulldown
        else
            return;
    }
}).disableSelection();

All it does is identify the div wrappers for the source and target uls. It then uses this to figure out if the target ul is above or below the source ul.

If it is below, it steps through all the wrappers from source to target, grabbing the first li from the succeeding wrapper and adding it to its end.

If it is above, the same thing happens, the only difference being it's picked from the end and added to the start.

If the source and target wrapper is the same, we don't need to do anything.


Fiddle - https://jsfiddle.net/ajdw2u0b/


Updates When Dragging

var source;
$(".sortable-queue").sortable({
    revert: true,
    scroll: false,
    connectWith: ".connected-sortable",
    placeholder: "item-dragable-placeholder",
    start: function(event, ui) {
        source = ui.item.parent().parent().parent();
    },
    over: function (event, ui) {
        var target = $(event.target).parent().parent();
        var children = target.parent().children();
        var start = children.index(source);
        var end = children.index(target);

        // same pulldown
        if (start === end) {
            console.log(start)
            return;
        }
        // bubble up?
        else if (start < end) {
            for (var i = start; i < end; i++) {
                $(children[i]).find("ul").append($(children[i + 1]).find("li:not(.ui-sortable-helper):not(.item-dragable-placeholder)").first().detach())
            }
        }
        // bubble down?
        else if (start > end) {
            for (var i = start; i > end; i--) {
                $(children[i]).find("ul").prepend($(children[i - 1]).find("li:not(.ui-sortable-helper):not(.item-dragable-placeholder)").last().detach())
            }
        }

        source = target;
    }
}).disableSelection();

The logic is pretty much the same. The only differences are that

  1. You do the updates on over (when you drag your item over a drop target).
  2. You have to maintain the source wrapper value instead of getting it from the ui object, since you'll be changing it once you hover over a drop target.

Note that you have to exclude the element being dragged and the place holder when picking an element to detach.


Fiddle - https://jsfiddle.net/f4655x9n/

like image 72
potatopeelings Avatar answered Nov 14 '22 22:11

potatopeelings