Search code examples
javascriptjquerydrag-and-dropjquery-ui-draggable

How to sort automatically a JQuery draggable box by item's data-attribute


I am trying to implement a behavior for my draggable items.

The behavior is described here :

  • My box contains multiple draggable items which are sorted on page load
  • I can drag and item from that box on a drop area
  • But if I drag it back to the box it should re-place at its original position based on its data-attribute

I have no clue how to achieve this. I saw that sortable could possibly do this but I don't know how to combine it with draggable.

Thank you

HTML

<div class="multiple-drag-area position-sticky sticky-top">
    <div class="box" data-position="1">
         Item 1
    </div>   
    <div class="box" data-position="2">
         Item 2
    </div>
    <div class="box" data-position="3">
         Item 3
    </div>    
</div>


<div class="drag-area">
   Drop 1
</div>
<div class="drag-area">
   Drop 2
</div>
<div class="drag-area">
   Drop 3
</div>

JS

$( ".box" ).draggable({
    scope: 'demoBox',
    revert: true,
    cursorAt: { top: 40, left: 40 },
    revertDuration: 100,
    start: function( event, ui ) {
           //Reset
           $( ".box" ).draggable( "option", "revert", true );
              console.log('-');
           }
    });

    $( ".multiple-drag-area" ).droppable({
        scope: 'demoBox',
        drop: function( event, ui ) {
            var area = $(this).find(".area").html();
            var box = $(ui.draggable).html()     
            $( ".box" ).draggable( "option", "revert", false );
                
            //Display action in text
            console.log("[Action] <b>" + box + "</b>" + " dropped on " + "<b>" + area + "</b>");
                
            //Realign item
            $(ui.draggable).detach().css({top: 0,left: 0, marginRight:4}).appendTo(this);  
        },
    })
            
    $( ".drag-area" ).droppable({
        scope: 'demoBox',
        drop: function( event, ui ) {
            var area = $(this).find(".area").html();
            var box = $(ui.draggable).html()     
            $( ".box" ).draggable( "option", "revert", false );
                
            //Display action in text
                console.log("[Action] <b>" + box + "</b>" + " dropped on " + "<b>" + area + "</b>");
                
            //Realign item
            $(ui.draggable).detach().css({top: 0,left: 0}).appendTo(this);
        },
        accept: function(draggable) {
             return $(this).find("*").length-1 == 0 && (!$(this).hasClass("blocked-seat"));
        }
    })

Solution

  • Something like that should work:

    $( ".box" ).draggable({
      revert: true,
    });
    
    $( ".drag-area" ).droppable({
      drop: function( event, ui ) {
        $(ui.draggable).detach().css({top: 0,left: 0}).appendTo(this);
      }
    });
    
    $( ".multiple-drag-area" ).droppable({
      drop: function( event, ui ) {
    
        let box = $(ui.draggable);
    
        // check if Element gets reinserted
        // we can check that by confirming the the box parent has the 'drag-area' class
        if($(box).parent().hasClass("drag-area")){
    
          $(box).detach().css({top: 0,left: 0}).appendTo(this);
    
          // sort the boxes based on 'data-position'
          $(this).find('.box').sort(function( a, b ) {
            return a.dataset.position - b.dataset.position;
          }).appendTo(this);
    
        }
      }
    });