How to prevent child sortables from cloning - javascript

I have three sortable list boxes, one grey and two yellow. I can drag and clone child sortable li's into the bottom yellow lists no problem.
The problem is that once the child clone li's from the grey list box are placed into the yellow list boxes, you can no longer drag/shift them within the containing yellow list box or to the adjacent yellow list box. They just clone continuously when you attempt to drag them elsewhere.
I would like to drag and clone sortables from the grey list box into the yellow boxes and have the cloned child li's be able to drag and move within the yellow lists boxes without cloning.
How can I prevent the child li's from cloning. Any help would be much appreciated. Thanks.
http://jsfiddle.net/equiroga/96hJj/
$(function() {
$(".sortable").sortable(
{
helper : "clone",
connectWith : ".sortable",
start : function(event,ui)
{
$(ui.item).show();
clone = $(ui.item).clone();
before = $(ui.item).prev();
position = $(ui.item).index();
},
beforeStop : function(event, ui)
{
if($(ui.item).closest('ul#sortable1').length>0)
$(this).sortable('cancel');
},
stop : function(event, ui)
{
if (position == 0) $("#sortable1").prepend(clone);
else before.after(clone);
}
});
$(".sortable").sortable();
});

You can set a particular behavior for the first list and a different for the others, the others can't interact with the first using a coonectWith selector with :not selector.
Code:
$(function () {
$("#sortable1").sortable({
helper: "clone",
connectWith: ".sortable",
start: function (event, ui) {
$(ui.item).show();
clone = $(ui.item).clone();
before = $(ui.item).prev();
position = $(ui.item).index();
},
beforeStop: function (event, ui) {
if ($(ui.item).closest('ul#sortable1').length > 0) $(this).sortable('cancel');
},
stop: function (event, ui) {
if (position == 0) $("#sortable1").prepend(clone);
else before.after(clone);
}
});
$(".sortable").sortable({connectWith: ".sortable:not('#sortable1')"});
});
Demo: http://jsfiddle.net/IrvinDominin/923VG/

Related

Draggable drops in wrong place on second drag

I'm trying to drag "templates" (initially as clones) from a "store" into droppable slots. Thereafter I want to be able to drop the dragged item into whatever slot I wish. Draggables are set as follows:
$( ".template" ).draggable({
helper: "clone",
}
);
$( ".item" ).draggable();
I got round the issue of the thing re-cloning by replacing the "template" class with an "item" class on first drop and within the drop function switching from a clone to a straight element as follows:
$( ".template-slot" ).droppable({
accept: ".template, .item",
drop: function( event, ui ) {
if(ui.draggable.hasClass("template")) {
$(this).append(ui.draggable.clone())
// add item class
$(".template-slot .template").addClass("item");
// remove template class
$(".item").removeClass("ui-draggable template");
// expand to fit the slot
$(".item").addClass("expand-to-slot");
// make it draggable again
$(".item").draggable();
}
if(ui.draggable.hasClass("item")) {
$(this).append(ui.draggable);
}
}
});
The first drag and drop works okay. the problem arises on the second drag and drop. The element drags okay but when dropped it gets placed twice as far in whatever direction I drag it. So, for example, if I've dragged it 50px to the right, it drops 100px to the right. the same with up and down etc.
I suspect it's something to do with the offset being cumulative but haven't worked out why it's doing this or how to fix it.
I've created a fiddle here: https://jsfiddle.net/stefem1969/a86jmnxu/10/ to show the problem.
Hope someone can help.
Working Example: https://jsfiddle.net/Twisty/fu83zq2y/11/
JavaScript
$(function() {
function makeDrag(obj, opt) {
if (obj.hasClass("ui-draggable")) {
obj.removeClass("ui-draggable");
obj.attr("style", "");
}
if (opt === undefined) {
opt = {};
}
obj.draggable(opt);
}
makeDrag($(".template, .item"), {
helper: "clone",
});
$(".template-slot").droppable({
accept: ".template, .item",
drop: function(event, ui) {
if (ui.draggable.hasClass("template")) {
console.log("it has the template class!");
var newItem = ui.draggable.clone();
newItem.toggleClass("template item");
newItem.addClass("expand-to-slot");
newItem.appendTo($(this));
makeDrag(newItem);
}
if (ui.draggable.hasClass("item")) {
console.log("it has the item class!")
var droppableOffset = $(this).offset();
console.log("droppableOffset: ", droppableOffset);
ui.draggable.attr("style", "").appendTo($(this));
}
}
});
$(".template-store, .thagomizer").droppable({
accept: ".item",
drop: function(event, ui) {
ui.draggable.remove();
}
});
});
Part of draggable is to set a top and left when moving it around. Even if you append the item to another element, it will still have those. Clearing that can help accomplish what you're looking for.
The makeDrag() is just helpful to perform the same operations a lot.
Hope this helps.

jQuery droppable zone in droppable zone

I have a droppable unordered list named <ul class="droppable">. Inside the unordered list I have dynamicly generated list items named <li class="placeholder"></li>, which are also droppable.
When dropping a draggable item between a placeholder, all works fine. But when dropping a draggable item on the <li class="placeholder"></li> itself, the draggable item gets appended to the placeholder and the unordered list.
So I get a double clone of it.
Here's a jsfiddle to have a visual example. I'm aware that it is logical that I get a double clone on my page, but I don't know how to disable it...
Any help is greatly appreciated!
UPDATE: If you drag a draggable item verticaly over a droppable element, they append automatically? How is that possible?
One approach you could try to solve this would be to move all your behaviors to your sortable and use droppable only to make a switch to determine the behavior you need. That way, you can handle everything at one place, which should resolve some of the unwanted effects of having both sortable and droppable on the same elements. Like this:
In your droppable you set the switch on over and out, and set the objects you need.
$("li.placeholder").droppable({
accept: "li.to-drag",
hoverClass: "correct",
activeClass: "active",
revert: false,
tolerance: "pointer",
over: function (e, ui) {
curDrag = ui.draggable;
curTarget = e.target;
curThis = $(this);
overSortable = true;
},
out: function (e, ui) {
overSortable = false;
},
drop: function (event, ui) {
}
});
Then, in beforeStop event of sortable, you check if the variable overSortable is true or not and set the behaviors there. All that refers to ui.draggable, this, and event.target will need to be replaced by the objects you set in droppable. An event better way would be to make a function handling all these behaviors and pass these as arguments.
beforeStop: function (e, ui) {
if (overSortable) {
curDrag.addClass('placeholder');
var dragging = curDrag.find("img").remove();
curThis.append(dragging).addClass("dropped");
curThis.droppable('disable');
var day = curDrag.attr('data-id');
var index = $(curTarget).index();
//...
} else {
ui.item.addClass('placeholder')
}
There are still adjusments to make, it's not completely clear what exactly should happen and when, but this can make it easier to work instead of trying to manage multiple events being fired when you drop an element, you only manage one.
http://jsfiddle.net/ts290vth/4/
Managed to get it working for you:
function dropping () {
$("li.placeholder").droppable({
accept: "li.to-drag",
hoverClass: "correct",
activeClass: "active",
revert: false,
tolerance: "pointer",
drop: function (event, ui) {
var dragging = ui.draggable.find("img");
$(this).append(dragging).addClass("dropped");
$(this).droppable('disable');
var day = $(ui.draggable).attr('data-id');
var index = $(event.target).index();
$(this).attr("data-id", day);
$(this).attr("date-order", index);
$(this).addClass("drop-"+ index);
$(this).attr("data-content", "");
NOTICE: the var dragging = ui.draggable.find("img"); this line was making you duplicated copies at drag and drop so you just had to remove the clone();.
it created clones of your dragged items at dest.
Now Notice this:
$("#dropzone").droppable({
accept: "li.to-drag",
revert: false,
tolerance: "pointer",
drop: function (event, ui) {
$(this).append(dragging);
//$("li.to-drag").addClass("placeholder");
$(this).droppable('disable');
}
});
}
Something with the placeholder class is causing you troubles.
Hope this helps.

Change items style when moving elements in jquery-ui sortable

I have two columns, that are jquery-ui sortable. In the left column are choosen items, in the right are all not choosen. You can move items (li elements) between both. Each element can be either in the left one or in the right column. The first element in the left column (choosen) is not sortable and cant be moved (name ID). Elements in the left column have different style as elements in the right column, also in the left column each li element is on self line, but in the right column they are floated.
Here is an image of that:
Everything is working fine, but when user is moving element between columns, I need the element change its style (while is moving = left mouse button is down), when is over left or right column. By changing its style I mean, when f.e. I start to move element Telefón from left to right, when I will be over right column, this element should change its background color to gray and display not in the whole line, but only in its width and ad versa.
Working demo
I cant figure out, how this can be done. Thanks.
You can add this to your sortable options:
over: function (event, ui) {
if ($(ui.item).closest("ul").hasClass("interested")) {
$(ui.item).detach().appendTo("#categories-source");
$(ui.item).css("width", "initial");
} else {
$(ui.item).detach().appendTo("#categories-chosen");
$(ui.item).css("width", "210px");
$(ui.item).css("height", "initial");
}
}
It puts dragged element in proper ul depending if previous one had class interested.
Update
Also set proper dimensions to the dragged element (because left list is one per line).
Fiddle
Since you are using jQuery UI, you can take advantage of droppable widget. This features two callback, over and out. Change CSS properties of draggable element whenever it is over or out droppable area.
See it working here.
$(".columns_all").droppable({
over: function (event, ui) {
ui.helper.css("background-color", "lightblue");
},
out: function (event, ui) {
ui.helper.css("background-color", "");
}
});
Update
As requested, here is a code for two-way dragging.
elwidth = $(".columns_selected li").width(); // used to set element width
// left to right
$(".columns_all").droppable({
over: function (event, ui) {
ui.helper.css({
"background-color": "lightblue",
width: "auto"
});
},
out: function (event, ui) {
ui.helper.css({
"background-color": "",
width: elwidth
});
},
drop: function (event, ui) {
ui.helper.css({
"background-color": "#ccc"
});
}
});
// right to left
$(".columns_selected").droppable({
over: function (event, ui) {
ui.helper.css({
"background-color": "red",
width: elwidth
});
},
out: function (event, ui) {
ui.helper.css({
"background-color": "",
width: "auto"
});
},
drop: function (event, ui) {
ui.helper.css({
"background-color": "#74ce9c"
});
}
});
Updated fiddle.

How to get element under mouse cursor while dragging another element?

I googled and found several answers but they was all about click or mousemove events which are not suitable for my question.
Basically I allow users to drag an item from a list and drop it on a folder in another list and I want to highlight element (in the folder list) whenever an item is dragging over it. Listening to mouseenter and mouseleave events on the folder list won't work. I tried with document.elementFromPoint in the dragging event (jQuery UI's Draggable drag) but unfortunately it returns the helper element instead of the element in the folder list. I think it's correct behavior since document.elementFromPoint returns the top most element under mouse cursor. But it doesn't solve my problem :(.
$("#filelist li").draggable({
helper: "clone",
drag: function (event, ui) {
console.log(event.pageX, event.pageY);
var element = document.elementFromPoint(event.pageX, event.pageY);
// element is helper element, instead of actual element under cursor which I want.
}
});
$("#folderlist").droppable({
drop: function (event, ui) {
}
});
// These mouse events won't be triggered while dragging an item.
$("#folderlist").on({
"mouseenter": function (event) {
this.style.backgroundColor = "#1c70cf";
},
"mouseleave": function (event) {
this.style.backgroundColor = "";
}
}, "li");
Apparently, the droppable has an hover function. http://jqueryui.com/droppable/#visual-feedback
$("#folderlist").droppable({
hoverClass: "ui-state-hover",
drop: function (event, ui) {
}
});
Then add this to your css :
.ui-state-hover
{
background-color: #1c70cf;
}

Sort multiple selected items in jQuery sortable?

I'm trying to select more than one item in a jQuery sortable set and then move the selected items around together.
Here's my weak beginning of an attempt to make it work. And here's the code:
HTML:
<div class="container">
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
<div>five</div>
</div>
JS:
$('.container div').draggable({
connectToSortable: '.container',
//How do I drag all selected items?
helper: function(e, ui) {
return $('.selected');
}
});
$('.container').sortable({
axis: 'y',
//How do I sort all selected items?
helper: function(e, ui) {
return $('.selected');
}
});
$('.container div').live('click', function(e) {
$(this).toggleClass('selected');
});
CSS:
body{background-color:#012;font-family:sans-serif;text-align:center;}
div{margin:5px 0;padding:1em;}
.container{width:52%;margin:1in auto;background-color:#555;border-radius:.5em;box-shadow:0 0 20px black;}
.container div{background-color:#333;color:#aaa;border:1px solid #777;background-color:#333;color:#aaa;border-radius:.25em;cursor:default;height:1em;}
.container div:hover{background-color:#383838;color:#ccc;}
.selected{background-color:#36a !important;border-color:#036 !important;color:#fff !important;font-weight:bolder;}
I don't know if I'm headed in the right direction or not. I can't find an example of this anywhere online. Just lots of related questions. Does anyone know how?
For example, if I've selected items 4 and 5 out of a list of 6. I want to be able to drag 4 and 5 to the top of the set to get this order - 4 5 1 2 3 6 - Or if I selected 5 and 1 and drag them to the bottom - 2 3 4 6 1 5
This seems to work with the multisortable plugin. Code below. Or see jsFiddle.
// ctrl + click to select multiple
$('.container').multisortable({
stop: function(e, ui) {
var $group = $('.ui-multisort-grouped').not(ui.item);
$group.clone().insertAfter($(ui.item));
$group.each(function() {
$(this).removeClass('ui-multisort-grouped');
});
$group.remove();
}
});
But what if multisortable breaks with future jQuery versions?
Modifying my answer here (according to your HTML and CSS) :
Select items to sort
Create a custom helper
Hide the selected items until sort is done
Resize the helper and placeholder according to the selection
Manually detach selected items from the current position and attach them to the new position after sort
Show the hidden items (undo step 3) after step5
$(function () {
$('.container').on('click', 'div', function () {
$(this).toggleClass('selected');
});
$(".container").sortable({
revert: true,
helper: function (e, item) {
if (!item.hasClass('selected')) item.addClass('selected');
var elements = $('.selected').not('.ui-sortable-placeholder').clone();
var helper = $('<div/>');
item.siblings('.selected').addClass('hidden');
return helper.append(elements);
},
start: function (e, ui) {
var elements = ui.item.siblings('.selected.hidden').not('.ui-sortable-placeholder');
ui.item.data('items', elements);
var len = ui.helper.children().length;
var currentHeight = ui.helper.height()
var itemHeight = ui.item.height() + 32; // 32 = 16x2 padding
ui.helper.height(currentHeight + (len * itemHeight))
ui.placeholder.height((len * itemHeight))
},
receive: function (e, ui) {
ui.item.before(ui.item.data('items'));
},
stop: function (e, ui) {
ui.item.siblings('.selected').removeClass('hidden');
$('.selected').removeClass('selected');
}
});
});
Updated Fiddle

Categories