29
votes

Im trying to develop a small page that has the following functionality:

  1. Page will have two divs: one being #origin, containing a lot of thumbnail images, and the second one will be #drop, where the images can be dropped on (up to 3 only).
  2. it needs to be possible to drag-drop the images from #drop back to origin.
  3. #drop needs to be sortable, since the order of the 3 selected images matter.
  4. When dropped on #drop, the images should align, as if they had original been displayed like that from the start.

I had never used jQuery before, and I even had a working page prototype before, where I already had the drag and drop between two containers through native HTML5 drag and drop events, but when I added the sortable() jquery functionality to #drop, the images could no longer be dragged back to #origin, which breaks my functionality.

So, I started over and wanted to implement both the drag & drop functionality as well as the sortable with jQuery. I've read pretty much the whole API for all draggable, droppable and sortable functionality, but I'm having trouble getting this to work. Not sure if it's the settings I'm using for each functionality.

On the fiddle, you can see that the images become draggable and I can see the settings I specify in effect (opacity, cursor), but the images cant be dropped on #drop.

From what I understand, the combination of making images draggable, the images having a "draggable" class, and making #drop droppable with an accepts of ".draggable", it should allow the most basic functionality, but even that doesn't seem to take. Also I read that if both draggable and droppable have the same "scope" setting, it should also work, but it isn't.

Here's a simple version of the page's code, plus a jsFiddle: http://jsfiddle.net/39khs/

Code:

css:

#origin {
  background-color: green;
}
#drop {
  background-color: red;
  min-height: 120px;
}

js:

$("img").draggable({ helper: "clone", opacity: 0.5, cursor: "crosshair", scope: "drop" });
$("#drop").droppable({ accept: ".draggable", scope: "drop"});
$("#drop").sortable();

html:

<div id="wrapper">
    <div id="origin" class="fbox">
        <img src="http://placehold.it/140x100" id="one" title="one" class="draggable" />
        <img src="http://placehold.it/140x100" id="two" title="two" class="draggable" />
        <img src="http://placehold.it/140x100" id="three" title="three" class="draggable" />
    </div>
  <p>test</p>
    <div id="drop" class="fbox">

    </div>
</div>

Any help is greatly appreciated.

2

2 Answers

67
votes

Here's the fiddle with the final result:

http://jsfiddle.net/39khs/82/

The key was to handle the "drop" event and manually removing the dropped image from #origin and add it to #drop. That is actually what I was doing on the first implementation, but did not know how to do it exactly with jQuery:

 $(dropped).detach().css({top: 0,left: 0}).appendTo(droppedOn);

I am not sure why jQuery does not really remove the dragged element from the old container and add it to the new one, but that is what had to be done for the newly dropped item to display correctly and not just exactly where it was dropped, disregarding css placing/styling.

The key line was by @Barry Pitman on the following SO question:

jQuery draggable + droppable: how to snap dropped element to dropped-on element

UPDATE: I had some spare time today and wanted to check if it was possible with just sortable, and lo and behold, it was.

http://jsfiddle.net/wj4ak/5/

just two lines of code allow dragging and dropping from one container to another, and sorting the items. They do not need to be inside ul/ol lists at all. The one annoying thing is that I did not intend to make the items on the origin div sortable, but it's something I can live with for now.

22
votes

If you want to use .droppable(), .draggable(), & .sortable(), then you only need to use .sortable().

Using your jsfiddle, I made the following changes:

HTML:

<div id="wrapper">
    <div id="origin" class="fbox">
    <ul id="sort1" class="list">
      <li><img src="http://placehold.it/140x100" id="one" title="one" class="draggable" /></li>
      <li><img src="http://placehold.it/140x100" id="two" title="two" class="draggable" /></li>
      <li><img src="http://placehold.it/140x100" id="three" title="three" class="draggable" /></li>
    </ul>
    </div>
  <p>test</p>
    <div id="drop" class="fbox">
      <ul id="sort2" class="list"></ul>
    </div>
</div>

JAVASCRIPT:

$( "#sort1, #sort2" ).sortable({
      helper:"clone", 
      opacity:0.5,
      cursor:"crosshair",
      connectWith: ".list",
      receive: function( event, ui ){ //this will prevent move than 3 items in droppable list
         if($(ui.sender).attr('id')==='sort1' && $('#sort2').children('li').length>3){
           $(ui.sender).sortable('cancel');
         }
      }
});

$( "#sort1,#sort2" ).disableSelection();

CSS:

#origin
{
  background-color: green;
}
#drop
{
  background-color: red;
  min-height: 120px;
}
.list
{ 
  min-height: 120px;
} 
.list li
{
 display: inline-block;
 list-style-type: none;
 padding-right: 20px;
}

DEMO: http://jsfiddle.net/39khs/80/

Hope this helps and let me know if you have any questions!