JQuery UI: Cancel Sortable upon Droppable Drop - javascript

I am using JQuery 1.5.1 and JQuery UI 1.8.11.
I have added sortable for a number of items - the task here is to allow drag to sort, this all works fine.
But I also want to incorporate droppable, so that the item can be dropped onto a "copy me" area - the task there will be to duplicate the item (I will work that bit out later)
Problem is the droppable target is at the bottom of the sortable list (I do not want to move this) and once the drop occurs the sortable item moves to the bottom of the list.
What I want to do is cancel this sort when the drop event fires.
You can see my problem in action here (just drag "Item 1" onto the "Drop to Copy Item" area and you will see the sort does not get cancelled)
As you can see I have tried the following in the droppable "drop" event (suggested from JQuery UI Docs) but it does not seem to work...
$(this).sortable('cancel');
I am also open to any other recommendations on how to achieve this "drop to copy" effect I am looking for.
Thanks

OK, so I have worked out a solution which does the job.
the cancel code does work if you have it in the "stop" event of the sortable function. However, it will only apply once the "revert" has completed. The problem is that I was trying to copy/revert the element from the droppable "drop" event and this was too early.
The solution is to wait for the "stop" event to complete, and to achieve this I had to create a "awaiting copy" flag, to be checked in the "stop" event.
Here is an example
It still doesn't feel right (UX-wise) but it works correct, and you could always set revert to false on the sortable function to get a slightly better feel.
The code from the example is as follows...
var itemCount = 3;
var awaitingCopy = false;
$(init);
function init() {
$("#Items").sortable({
revert: true,
placeholder: "ItemPlaceHolder",
opacity: 0.6,
start: StartDrag,
stop: StopDrag
});
$("#CopyItem").droppable({
hoverClass: "CopyItemActive",
drop: function(event, ui) {
awaitingCopy = true;
}
});
$("#NewItem").click(function(e) {
e.preventDefault();
itemCount++;
var element = $("<div class='Item'>Item " + itemCount + "</div>");
$("#Items").append(element);
element.hide().slideDown(500);
});
}
function CopyItem(element) {
awaitingCopy = false;
var clone = element.clone();
$("#Items").append(clone);
clone.hide().slideDown(500);
}
function StartDrag() {
$("#NewItem").hide();
$("#CopyItem").show();
}
function StopDrag(event, ui) {
if (awaitingCopy) {
$(this).sortable('cancel');
CopyItem($(ui.item));
}
$("#NewItem").show();
$("#CopyItem").hide();
}
Anyway, hopefully this will help others who want the same kind of effect... no stealing my design though ;)

Related

Change the dragged object when draggable begins

I am trying to create some thing like a UI designer. So I have a 'toolbox' and a 'canvas'. When I mark the tool box items as draggable, I am able to drag them. But what I need is to drag a copy, so that I can drag multiple instances of a tool item to the canvas.
$('.tool-box-item').draggable({
helper : 'clone',
drag : function (event, ui){
// jQuery does not allow object modification here
},
});
This does not work as the cloning is applied only while dragging and as soon as the drag is complete, the original element moves. I tried overriding the drag property, but I am unable to modify the dragged element. Same was the case with start.
Note : I can not use the stop event as I want to create the copy when the drag starts and not after it. I was also able to create a copy by defining the canvas as droppable and making a copy there, But then again I want to create a copy when the drag starts.
I think I will need to create a custom drag function but was hoping if jQuery has any other alternative way.
Here is a jsfiddle, but I want to clone the element at the start and not after the drag.
EDIT : The destination is a 3rd party control. It has its own implementation for drop event (stacking and aligning). In case I clone it at the end I will have to modify their implementation.
To keep "dragend" event, try to use original element for dragging, and clone it for place to your "toolbox".
var graggableConfig = {
helper:"clone",
stop: function(event, ui){
var clon = $(this).clone();
clon.attr("style","")
.appendTo(".src")
.draggable(graggableConfig);
$(this).detach()
.attr("style","")
.appendTo(".dst")
.draggable( "destroy" );
}
};
$(".item").draggable(graggableConfig);
codepen here
You can use jquery's clone (https://api.jquery.com/clone/) method to clone your draggable element once it is dropped. Would something like this meet your needs?
$( function() {
makeDraggable($( "#draggable" ));
});
function makeDraggable(element) {
$(element).attr('style', 'position:absolute').draggable({
start: function() {
makeDraggable($('#draggable').clone().attr('style', '').appendTo('body'));
}
});
}
Its a little rough but here is the jsbin: https://jsbin.com/tasodixoka/edit?html,output
Edit: I updated your fiddle: https://jsfiddle.net/z9rrL6po/6/

Is there an event called while a jQuery UI draggable element is being reverted to its original position?

I have a draggable element using jQuery UI, and have a function attached the drag event that gets continuously called with the element's position while the user is dragging it.
I also have revert: true set on this element, so when the user stops dragging the element springs back to its original position. Is there a way I can attach a listener to perform the same functionality as when it's being manually moved?
I can't see anything specifically related to the revert property in the docs, so if that's not possible is there a more general event called while an element is moving?
Thanks!
It appears there's nothing in the API for this, so I've had to hack it using setInterval.
var revertInterval;
var revertEvent = function(el) {
revertInterval = setInterval(function() {
// Do things
// e.g. console.log(el.position().top);
}, 5);
}
$('#draggable').draggable({
revert: function() {
revertEvent($(this));
return true;
},
stop: function() {
clearInterval(revertInterval);
}
});
the revert is accept function as follow
revert: function(param){}
DEMO

jQuery draggable problem with 'scroll' on object inside overflow:hidden container when using appendTo: 'body'

I have a draggable object, with that can be accepted in several droppables.. I have put all the droppables in a container, and simply want to be able to detect when the draggable is hovering over the container of droppables...
At first, I tried making use of the 'over' and 'out' callbacks for droppables, but it was not working because hovering from one droppable to another (inside the same container) was causing it to think the mouse had left the container...
So my next approach was to in the drag start callback, do an event listener for mouseenter and mouseleave on the container-- and then stop listening on the drag stop callback...
However, this results in total crazy behavior... If you look at my example page:
http://collinatorstudios.com/www/jquery_draggable_test.html
When dragging the box to the red dropzone, you should see "enter" when the mouseenter event fires, and "leave" when mouseleave happens.. However, just dragging the box over the inside of the container causes "leave" to appear a zillion times..... I cannot figure out why this is happening, nor what solution there is to my problem so I can do what I need to. I've been working on this for almost 4 hours now and am losing my mind over what seems like it should be so simple to achieve.
Any help would be greatly appreciated... Thanks.
Try adding a droppable for the container:
$('#drop_zone_container').droppable({
over: function(){ feedback.text('enter')},
out: function(){feedback.text('leave')}
});
You only need to bind to the events once! There is no need to bind and unbind them each time... I separated them out in the code below to make it more clear about binding once.
And as ZDYN said (+1 to him), you need to include a droppable code, but instead of using the container, use the zones inside... here is a demo and the full code below.
var feedback = $('#feedback');
$('.item').draggable({
revert: true,
zIndex: 999,
cursor: 'move'
});
$('.drop_zone').droppable({
drop: function(event, ui) {
ui.draggable.appendTo($(this));
}
}).bind('dropover dropout', function(e) {
var id = this.id;
feedback.text(e.type === 'dropover' ? 'Over: ' + id : 'Out: ' + id);
});

Jquery Sortable, delete current Item by drag out

My Problem:
The sortable event out: fires when I drag something in the list or when I sort the list.
But I only want to start the function when I drag an item out.
My code
$(document).ready(function ust()
{
$('#div1').sortable({
out: function(event, ui) { $('#nfo').append('OUT<br />'); }
});
});
Working example
http://jsfiddle.net/FrbW8/22/
Use beforeStop to intercept the item and remove it:
receive: function(e, ui) { sortableIn = 1; },
over: function(e, ui) { sortableIn = 1; },
out: function(e, ui) { sortableIn = 0; },
beforeStop: function(e, ui) {
if (sortableIn == 0) {
ui.item.remove();
}
}
(I originally found this in the Google, but can no longer find the link. So, I apologize for not referencing the source.)
This is the default behaviour of the out callback. See this jquery ui trac ticket
I really do not agree with the 'logical' behaviour notion.
"However, note that the "out" callback
will still be triggered if you drag
into a list and then release the mouse
(but not if you're not over the list).
This is logical behaviour and happens
for normal sortables as well."
The other solutions doesn't seems to work with connected sortable lists so I'm posting my own solution that works perfectly for my case. This makes use of the "droppable" plugin capturing the "drop" event when the elements are dropped outside of the sortable lists.
$('#div1').sortable({
....
}).droppable({greedy: true})
$('body').droppable({
drop: function ( event, ui ) {
ui.draggable.remove();
}
});
Here is a jsfiddle of this approach in action: http://jsfiddle.net/n3pjL/
I use this.element.find('.ui-sortable-helper').length to make difference between "sort out event" and "drop out event". When you are sorting, sorted item has class ui-sortable-helper. After drop there is no other ui-sortable-class until you start sorting again (at least in my script). Hope to help someone.

js/jQuery Drag'n'Drop, recalculate the drop targets

I have the following issue, I have a large tree which has subnodes which can be folded and unfolded on demand (the data within nodes gets fetched with AJAX). However, I use jquery.event.drop/drag to create my drag/drop targets.
However, when I fold/unfold the drop targets change position and I need to recalculate. This is how I wanted to do that:
function create_drop_targets() {
$('li a')
.bind('dropstart', function(event) {
})
.bind('drop', function(event) {
})
.bind('dropend', function(event) {
});
}
create_drop_targets() is called upon fold/unfold.
However, this doesn't work. I have located the following within jquery.event.drop:
var drop = $.event.special.drop = {
setup: function(){
drop.$elements = drop.$elements.add( this );
drop.data[ drop.data.length ] = drop.locate( this );
},
locate: function( elem ){ // return { L:left, R:right, T:top, B:bottom, H:height, W:width }
var $el = $(elem), pos = $el.offset(), h = $el.outerHeight(), w = $el.outerWidth();
return { elem: elem, L: pos.left, R: pos.left+w, T: pos.top, B: pos.top+h, W: w, H: h };
}
Now I need to know how I can call the setup() method again so it repopulates $elements with the new positions for the droppables.
Just had the same issue. I wandered around within the source-code of jQuery and found this (in ui.droppable.js):
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
...
So, you'd just have to use
$(".cocktails").draggable({
refreshPositions: true,
});
Seems not to be documented very much... but it fixed my problem. Makes everything a bit slower of course, I would advise some usage-dependent tweaking (enable it before the changes occur, and disable it once the user has moved his mouse and the changes have occured).
Maybe it will be better to add live events introduced in jQuery 1.3?
$("li a").live("dropstart", function(){...});
I ran into the same issue when I tried to combine scrolling with draggable rows in liteGrid, but I found a work-around. Your mileage may vary, but what I did was add logic to my drag event handler that would check to see if the grid was being scrolled (which is when I needed to force the droppable positions to be refreshed), and if so, I set refreshPositions to true on the draggable. This doesn't immediately refresh the positions, but it will cause them to refresh the next time the drag handle moves. Since refreshPositions slows things down, I then re-disable it the next time my drag event handler fires. The net result is that refreshPositions is enabled only when the grid is scrolling in liteGrid, and its disabled the rest of the time. Here's some code to illustrate:
//This will be called every time the user moves the draggable helper.
function onDrag(event, ui) {
//We need to re-aquire the drag handle; we don't
//hardcode it to a selector, so this event can be
//used by multiple draggables.
var dragHandle = $(event.target);
//If refreshOptions *was* true, jQueryUI has already refreshed the droppables,
//so we can now switch this option back off.
if (dragHandle.draggable('option', 'refreshPositions')) {
dragHandle.draggable('option', 'refreshPositions', false)
}
//Your other drag handling code
if (/* logic to determine if your droppables need to be refreshed */) {
dragHandle.draggable('option', 'refreshPositions', true);
}
}
$("#mydraggable").draggable({
//Your options here, note that refreshPositions is off.
drag: onDrag
});
I hope that saves you from ramming your head into the keyboard as many times as I did...
I realize the original question is quite old now, but one little trick I came up with to refresh the position of draggable elements without much overhead (AFAICT) is to disable and immediately re-enable them wherever appropriate.
For instance, I noticed that resizing my browser window would not refresh the position of my draggable table rows, so I did this:
$(window).resize(function () {
$(".draggable").draggable("option", "disabled", true);
$(".draggable").draggable("option", "disabled", false);
});
I hope this helps someone out there!

Categories

Resources