16

I'm working on a project where I'm dragging elements from a 3rd party jQuery control to a jQuery sortable, using a combination of droppable and sortable.

This works perfectly fine, except the item being added is always added to the bottom of the sortable list, and you must then move it to the correct location as a separate step.

Is it possible to have the item added to the location where you dropped it in the list?

You can see this behavior in the jQuery shopping card droppable demo from here. Here is a jsfiddle of the same code. As you add items from the products to your cart at the bottom, it always adds at the bottom, even if you drop it near the top.

Here's the jQuery code:

     $(function () {
     $("#catalog").accordion();
     $("#catalog li").draggable({
         appendTo: "body",
         helper: "clone"
     });
     $("#cart ol").droppable({
         activeClass: "ui-state-default",
         hoverClass: "ui-state-hover",
         accept: ":not(.ui-sortable-helper)",
         drop: function (event, ui) {
             $(this).find(".placeholder").remove();
             $("<li></li>").text(ui.draggable.text()).appendTo(this);
         }
     }).sortable({
         items: "li:not(.placeholder)",
         sort: function () {
             $(this).removeClass("ui-state-default");
         }
     });
 });
AaronS
  • 7,649
  • 5
  • 30
  • 56

2 Answers2

23

use droppable's drop event's callback to compare the current top offset position of the draggable helper with the top offset of every element already present or previously added in the droppable

drop: function (event, ui) {

if($(this).find(".placeholder").length>0)  //add first element when cart is empty
{
    $(this).find(".placeholder").remove();
    $("<li></li>").text(ui.draggable.text()).appendTo(this);
}

else
{

    var i=0; //used as flag to find out if element added or not

    $(this).children('li').each(function()
    {
        if($(this).offset().top>=ui.offset.top)  //compare
       {
          $("<li></li>").text(ui.draggable.text()).insertBefore($(this));
          i=1;   
          return false; //break loop
       }
    })

    if(i!=1) //if element dropped at the end of cart
    {
        $("<li></li>").text(ui.draggable.text()).appendTo(this);
    }

}

} 

DEMO WITH CODE

FULL SCREEN DEMO

Udit Bhardwaj
  • 1,761
  • 1
  • 19
  • 29
17

What about doing this? Using both the connectToSortable AND connectWith options works, I think. There might be a more clever way to hide/show the placeholder, but this definitely works.

$(function () {
    $("#catalog").accordion();
    $("#catalog li").draggable({
        appendTo: "body",
        helper: "clone",
        connectToSortable: "#cart ol"
    });
    $("#cart ol").sortable({
        items: "li:not(.placeholder)",
        connectWith: "li",
        sort: function () {

            $(this).removeClass("ui-state-default");
        },
        over: function () {
            //hides the placeholder when the item is over the sortable
            $(".placeholder").hide(); 

        },
        out: function () {
            if ($(this).children(":not(.placeholder)").length == 0) {
                //shows the placeholder again if there are no items in the list
                $(".placeholder").show();
            }
        }
    });
});

Work Demo in Fiddle

KyleMit
  • 30,350
  • 66
  • 462
  • 664
tedwards947
  • 380
  • 1
  • 9
  • +1 great solution as it also incorporates having a placeholder, which makes it obvious to the user that their selected item is also being sorted on drop. – KyleMit Nov 21 '13 at 19:13
  • Good solution. However, the 3rd party control I'm using doesn't implement the connectTo from jQueryUI so I'm unable to use it. I tried to indicate that in my question, but didn't go into too much detail so there wouldn't be too many questions about the control itself. Thank you for the assistance. – AaronS Nov 29 '13 at 21:28
  • 1
    This is a great solution, but the fiddle example does not show the placeholder after I move out of the droppable area. – Karl Gjertsen Aug 27 '14 at 11:57