I'm trying to create a simple sortable and draggable list composed of :
I managed to do this so far : JSFiddle
HTML Code :
<h3>DRAGGABLE</h3>
<ul id="draggables">
<li class="draggable">Item 1</li>
<li class="draggable">Item 2</li>
<li class="draggable">Item 3</li>
<li class="draggable">Item 4</li>
<li class="draggable">Item 5</li>
</ul>
<h3>SORTABLE</h3>
<ul id="itemsContainer">
<li class="item">Item 1</li>
<li class="item">
<p>Item with drop zone</p>
<div class="droppable">DROP HERE</div>
</li>
<li class="item">Item 3</li>
<li class="item">Item 4</li>
<li class="item">Item 5</li>
</ul>
The JS Code is here :
$(".draggable").draggable({
connectToSortable: '#itemsContainer',
helper: 'clone',
revert: 'invalid'
});
$("#itemsContainer").sortable({
revert: true
});
$(".droppable").droppable({
drop: function(event, ui) {
$(this).html("Dropped!");
$(this).css("background-color", "red");
$(ui.helper[0]).css("background-color", "green");
}
});
The problem is, when an item is dropped inside the droppable div, it detects correctly that a drop is happening, but the item is still added to the sortable list. I thought the "greedy" option would prevent this from happening. Any ideas ?
EDIT : If I apply a styling to the ui.helper[0] object (aka the cloned draggable object), it stays on until the item is actually sorted in the sortable list.
Might be a bit of overkill, but this is what I could come up with.
When we stop
dragging a draggable item, we need to see if the item is over a droppable area, if so append it to said area.
Edit: Now we can drop a sortable item from the same list into the drop zone by connecting the sortable list to the drop zone and accepting the sortable items.
var over = false; // is the item over a droppable area?
var el_over = null; // if over a drop area, what drop area?
var de = null; // the item to detach
$(".draggable-item").draggable({
connectToSortable: ".list-2",
stop: function(event, ui) {
if (over) {
de = $(this).detach();
el_over.append(de);
}
over = false;
el_over = null;
}
});
$(".list-2").sortable({
connectWith: '.drop-zone, .list-2',
cursor: "move",
stop: function(event, ui) {
if (over) {
de = ui.item.detach();
el_over.append(de);
}
over = false;
el_over = null;
}
});
$(".drop-zone").droppable({
accept: ".draggable-item, .sortable-item",
over: function(event, ui) {
//console.log("over");
over = true;
el_over = $(this);
},
out: function(event, ui) {
//console.log("out");
over = false;
el_over = null;
}
});
ul {
padding: 10px;
list-style-type: none;
width: 200px;
}
li {
text-align: center;
padding: 5px 10px;
margin: 5px;
}
.draggable-item {
background: #9bcdf0;
}
.sortable-item {
background: #6c2020;
}
.drop-zone {
min-height: 30px;
background: #fff;
padding: 1px 0;
}
.drop-zone .draggable-item {
width: auto !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<ul class="list-1">
<li class="draggable-item">draggable 1</li>
<li class="draggable-item">draggable 2</li>
<li class="draggable-item">draggable 3</li>
<li class="draggable-item">draggable 4</li>
<li class="draggable-item">draggable 5</li>
</ul>
<ul class="list-2">
<li class="sortable-item">sortable 1</li>
<li class="sortable-item">sortable 2</li>
<li class="sortable-item">sortable 3
<div class="drop-zone"></div>
</li>
<li class="sortable-item">sortable 4</li>
<li class="sortable-item">sortable 5</li>
</ul>
Updated answer:
Turns out we don't need all that overkill XD. If we just initialize the .drop-zone
as a sortable and connect it to itself and list-2
we get the same outcome.
Here is the updated fiddle.
$(".draggable-item").draggable({
connectToSortable: ".list-2",
});
$(".list-2").sortable({
connectWith: '.drop-zone, .list-2',
cursor: "move",
});
$(".drop-zone").droppable({
accept: ".draggable-item, .sortable-item",
});
$(".drop-zone").sortable({
connectWith: '.drop-zone, .list-2',
items: '.draggable-item, .sortable-item',
});