Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JQuery sortable list with some elements frozen in order?

I am looking for a method to have a JQuery-like sortable list (link), but with some of the elements in fixed undraggable positions.

It's trivial to make some elements undraggable, but their positions don't remain fixed because the number of draggable elements above and below them can change.

To be more specific: I have a list of 10 items in rank order. I want the user to be able to change the rank for some of the items by dragging (a la sortable list), but not change the rank of other frozen elements. The standard JQuery sortable undraggable feature allows the rank of frozen items to be changed by changing the number of elements above or below the frozen ones.

I've tried do this manually by "swapping" list elements when dragging. I.e., when a list element is dragged over a non-frozen element, the position of the two elements is swapped. This gives the semantics I want, but the item being dragged visibly "jumps" between its new position and the current 'drag' position. (I want it to remain in its current drag position; only its position in the DOM should change. But when the DOM position changes, the drag offset coordinates must be recomputed. And I don't know how to do both the DOM position change and drag offset coordinate change atomically, preventing a redraw between the two. Currently, sometimes a redraw happens between the two, and the element visibly, albeit shortly, jumps.)

Edit Here's a jsfiddle showing my manual approach: link. Play with it for a while and you'll notice the flicker/jumping I mentioned.

like image 744
David B. Avatar asked Feb 16 '12 20:02

David B.


3 Answers

I've looked at your fiddle, had another think, and here's one way to do it using sortable: on start store the fixed index positions in the data object of each fixed item, and then on any change, move the fixed items back into place.

$(function() {   
    $('#rankings').sortable({
        axis: 'y',
        items: '.sortable',
        start: function () {
            $(this).find("li:not(.sortable)").each(function () {
                $(this).data("fixedIndex", $(this).index());
            });
        },        
        change: function () {
            $(this).find("li:not(.sortable)").each(function () {
                $(this).detach().insertAfter($("#rankings li:eq(" + ($(this).data("fixedIndex")-1) + ")"));
            });
        }
    });
});

Using a list such as:

<ol id="rankings">
    <li class="sortable">Dog</li>
    <li class="">Cat</li>
    <li class="sortable">Parrot</li>
    <li class="sortable">Gerbil</li>
    <li class="sortable">Snake</li>
    <li class="">Goldfish</li>
</ol>

Seems to work.

like image 123
graphicdivine Avatar answered Nov 07 '22 18:11

graphicdivine


I think I might have cracked it - http://jsfiddle.net/q5c4Y/3/. Quite a challenge though. A few things helped along the way

  • Using more structured markup
  • Using droppable's over event to swap list items, but not the drop event (using dragstop instead)
  • Setting some positioning on inner items in order to keep them in the right position after changing their parent element

Although, reading @graphicdivine's answer above, it looks like what you really needed all along is jquery ui sortable

like image 30
wheresrhys Avatar answered Nov 07 '22 18:11

wheresrhys


1) If the items you do not want changeable are all at the top and/or bottom of the list, you can create those items outside of the list, thereby allowing only of the sorting of OTHER items and preserving the rankings of the immutable ones.

2) Can you use the "stop" event to check that the rankings of the ones you want has been preserved? ie, check that there are still only 3 items above the 4th ranking item, and if not, move the new "4th" below the one that has an immutable rank of 4th? Basically, force a re-shifting of items after the item has been dragged into an illegal spot?

like image 37
Rebecca Avatar answered Nov 07 '22 17:11

Rebecca