Multiple drag and drop in JQuery: event after dragging - javascript

I am actually trying to make a .php page where I am going to have 3 draggable elements which have to be dragged to 3 droppable elements -each draggable to a droppable, and they are unique, so one each droppable will only accept a certain draggable.
The thing is that I need to control that all of the elements have been dragged to the right spot, in which case I should redirect the user for example to success.php, otherwise, if some of the elements were dragged to the wrong droppable, the user have to go to for example failure.php.
Is there anyway for example to save a certain value in a $_SESSION in PHP in order to know that all the draggables have been dropped in the right place?
This is the code for the drag&drop:
$(function() {
$("#draggableLeiden").draggable();
$("#draggableTheHague").draggable();
$("#draggableRotterdam").draggable();
$("#droppableLeiden").droppable({
accept: '.imgLeiden',
drop: function(event, ui)
{
$(this).addClass('ui-state-highlight');
}
});
$("#droppableTheHague").droppable({
accept: '.imgTheHague',
drop: function(event, ui)
{
$(this).addClass('ui-state-highlight');
}
});
$("#droppableRotterdam").droppable({
accept: '.imgRotterdam',
drop: function()
{
$(this).addClass('ui-state-highlight');
//var activeClass = $(this).droppable('option', 'activeClass','ui-state-highlight');
}
});
});
I am trying to do this for example getting the active class of the droppable elements to see if it matches 'ui-state-highlight', but actually, that tag is gonna be executed everytime the page reloads, so if I try to insert any code into the
drop: function()
it will always execute.
Thanks a lot in advance!

Use the revert option instead. When a draggable element is dropped on anything but the accepted droppable element then it will slide back to its original spot. Does this work for you?

Are you by chance looking for ui.draggable it holds the element that was dropped?
http://jqueryui.com/demos/droppable/
$('#target').droppable({
drop : function(event,ui){
if($(ui.draggable).attr('id') == "correcttarget")
{
window.location = "yay.php";
}
else
{
window.location = "awwww.php";
}
}
})

Related

Jquery UI. Droppable after element

I have an issue regarding jquery Ui droppable.
I need to drop an element before or after the element it's dragged over. Is there anyway to detect which element it's over, and then append/prepend it in the parent container.
Right now, I just drops in the end of the div, because of append, I'm a little clueless.
I made a fiddle to hopefully better illustrate my issue:
http://jsfiddle.net/tsxzf80u/
$('section').droppable({
accept:'.dragme',
drop:function(event, ui){
var div = $("<div />").addClass('full').html('test');
$(this).append(div);
}
});
Thanks in advance
If you make each of the divs inside of the section individually droppable, then use after(), seems to do what you want.
$('#fullId1, #fullId2').droppable({
accept: '.dragme',
drop: function (event, ui) {
var div = $("<div />").addClass('full').html('test');
$(this).after(div);
}
});
http://jsfiddle.net/tsxzf80u/2/

jQuery: how to enable droppable again

I want it to be possible to drag (draggables) several HTML elements into several other HTML elements. (droppables)
I found the jQuery UI draggable/droppable.
Currently, I define all li elements inside an ul as draggable. I also have three divs which accept all the draggable li elements.
<ul>
<li>foo</li>
<li>bar</li>
<li>baz</li>
</ul>
Defining them as draggable:
$('li').draggable({ revert: 'invalid' });
My droppable divs:
<div class="droppable" id="div1"></div>
<div class="droppable" id="div2"></div>
<div class="droppable" id="div3"></div>
Make the divs droppable:
$('div.droppable').droppable({ accept: 'li', drop: funcDrop, out: funcOut });
I now want that only one li element can be dropped into a div at the same time. I am trying to do that by disabling the droppability inside the div element I dropped the draggable, like so:
funcDrop = function(event, ui) {
$(this).droppable('disable');
}
This works. Now I am not able drop any further li elements into this div. The problem is, how/when to enable the div again? Clearly if the contianing draggable was removed. So I tried to enable the div inside funcOut:
funcOut= function(event, ui) {
strIdValue = event.target.id;
$('#' + strIdValue).droppable('enable');
}
But this does not work.
It seems also that I can't work with $(this) selector inside the funcOut.
Does anyone have a hint?
Best regards
I use jQuery 2.1 and UI 1.10.4
I figured out an alternative solution:
It seems if an element once was disabled, the out event does not fire and I am therefore not able to enable it again using the element itself. So what I did, is this:
If you drop a draggable element on a droppable element, the drop event is triggered and does this:
$(this).droppable({ accept: ('#' + $(ui.draggable).attr('id')) });
So the droppable is not disabled, but accepts only the received draggable by its unique HTML id. Because it is not disabled the out event will now be triggered. This is what I do in it:
$(this).droppable({ accept: 'li' });
Now the droppable accepts all HTML li elements as ever. Now each droppable accepts only one draggable. The droppable is now "locked" if it receives a draggable.
I wonder if this is a valid solution or just a dirty hack.
(Possible duplicate/related: jQuery UI - Droppable only accept one draggable)
Based on the JSFiddle you've shared (which currently is not doing what you asked), here is a modified version of the droppable function that you can use to achieve the desired result:
$(".droppable").droppable({
accept: "li",
drop: function(event, ui) {
$(this).droppable('option', 'accept', ui.draggable);
},
out: function(event, ui){
$(this).droppable('option', 'accept', 'li');
}
});
As the other answer stated, the trick is to make our droppable div accept 'ui.draggable', being ui the item that is currently being dragged to our droppable div.
Hope that helps!
(EDITED: Grammar)

jQuery drag and drop - checking for a drop outside a droppable

My apologies if this was answered in another question, I could not find an answer specific to my problem!
I'm trying to test whether a jQuery draggable is being dropped outside of a legal droppable. This would normally be solved 90% of the time by reverting the draggable, but I don't want to do that. Instead, I want to do one thing if the draggable is dropped onto a droppable (working great!), and something else if it is dropped outside of all possible droppables (currently getting the better of me!).
In a nutshell:
jQuery('#droppable').droppable(
{
accept: '#draggable',
drop: function(event, ui)
{
// awesome code that works and handles successful drops...
}
});
jQuery('#draggable').draggable(
{
revert: false,
stop: function()
{
// need some way to check to see if this draggable has been dropped
// successfully or not on an appropriate droppable...
// I wish I could comment out my headache here like this too...
}
});
I feel like I'm missing something really obvious...thanks in advance for any help!
Because the droppable's drop event fires before the draggable's stop event, I think you can set a flag on the element being dragged in the drop event like so:
jQuery('#droppable').droppable(
{
accept: '#draggable',
drop: function(event, ui)
{
ui.helper.data('dropped', true);
// awesome code that works and handles successful drops...
}
});
jQuery('#draggable').draggable(
{
revert: false,
start: function(event, ui) {
ui.helper.data('dropped', false);
},
stop: function(event, ui)
{
alert('stop: dropped=' + ui.helper.data('dropped'));
// Check value of ui.helper.data('dropped') and handle accordingly...
}
});
I see that you already got an answer; anyway I had this same problem today and I solved it this way:
var outside = 0;
// this one control if the draggable is outside the droppable area
$('#droppable').droppable({
accept : '.draggable',
out : function(){
outside = 1;
},
over : function(){
outside = 0;
}
});
// this one control if the draggable is dropped
$('body').droppable({
accept : '.draggable',
drop : function(event, ui){
if(outside == 1){
alert('Dropped outside!');
}else{
alert('Dropped inside!');
}
}
});
I needed that because I couldn't change the options of my draggables, so I had to work only with droppables (I needed it inside the awesome FullCalendar plugin).
I suppose it could have some issues using the "greedy" option of droppables, but it should work in most cases.
PS: sorry for my bad english.
EDIT: As suggested, I created the version using the jQuery.data; it can be found here : jsfiddle.net/Polmonite/WZma9/
Anyway jQuery.data documentation say:
Note that this method currently does not provide cross-platform support for setting data on XML documents, as Internet Explorer does not allow data to be attached via expando properties.
(meaning that it doesn't work on IE prior to 8)
EDIT 2: As noted by #Darin Peterson , the previous code doesn't work with more than one drop-area; this should fix that issue: http://jsfiddle.net/Polmonite/XJCmM/
EDIT 3: Example from EDIT 2 has a bug. If I drag "Drag me!" to the bottom droppable, then drop "Drag me too" to the upper droppable and then drop "Drag me too" outside, it alerts "Dropped inside!" So, don't use it.
EDIT 4: As noted by #Aleksey Gor, the example in Edit 2 was wrong; actually, it was more of an example to explain how to loop through all the draggables/droppables, but I actually forgot to remove the alert messages, so it was pretty confusing. Here the updated fiddle.
Try to use the event "out" of a droppable element.
This is the documentation
"This event is triggered when an accepted draggable is dragged out (within the tolerance of) this droppable."
If I'm right, this is what you need.
What is also possible is to create an element overlay over the whole page. If the element is dropped there you fire your event. Not the best, but I think the only way to do it. Because you need some other "droppable" item to fire these events.
The advantage of the following example, is that you don't need to change or know about the droppable code:
The draggable revert property can have a function(value){}. A value is passed as argument, indicating if helper was dropped onto an element (the drop element), or 'false' if not dropped on an element (drop outside or not accepted).
Note: you need to return the correct bool value from that
revert-function, in order to tell the helper to revert or not
(true/false). True means yes, take the helper back to its original
position, by moving it back in a slow motion (out-of-the-box). False means no, just
remove the helper abdruptly. Setting the revert property to 'invalid',
is a shortcut of saying
'yes, if dropped outside, then revert helper'
'no, if dropped on a droppable element and accepted, then kill the helper right away'.
My guess is that you can add current ui helper to draggable container with data property during start event. Then pick it up in the revert function from the data property. Then add a property to the helper, indicating if it was dropped or not. Then ultimately in the stop event, check this data property value, and do what you intended.
Order of event/function calls for draggable:
start-revert-stop
This could be an example:
jQuery('#draggable').draggable(
{
start: function(event, ui) {
$(this).data('uihelper', ui.helper);
},
revert: function(value){
var uiHelper = (this).data('uihelper');
uiHelper.data('dropped', value !== false);
if(value === false){
return true;
}
return false;
},
stop: function(event, ui)
{
if(ui.helper.data('dropped') === true){
// handle accordingly...
}
}
});
You can even return true in the revert function, and just remove the helper during the stop event instead, depending on the data('dropped') value with ui.helper.remove(). Or you could even explode it with CSS3 if you still have a bad day ;)
I add the solution I adopted since you can understand this very easily from the css classes of the object you are moving:
jQuery('#draggable').draggable({
stop: function(event, ui) {
if (ui.helper.hasClass('ui-draggable-dragging')) {
console.log('dropped out');
} else {
console.log('dropped successfully');
}
}
});
Old question and old answers mean that this "may" be a new solution. You (maybe) also wanted, as the question states, to know IF a draggable was dropped outside of a droppable. For me, in at least 95% of the cases, I don't really care IF, I just want things to go back to how they were without any changes being made WHEN that happens.
Setting revert to the string invalid accomplishes the desired behavior without any extra code or funky things to do.
$( '#draggable' ).draggable({
revert: "invalid",
stop: function( event, ui )
{
// whatever
}
});
Again, it won't tell you "if it was dropped outside of a droppable," but it will revert to the initial state if that happens.
This work for me:
<script>
$( function() {
$( "#draggable" ).draggable();
$( ".droppable" ).droppable({
drop: function( event, ui ) {
if( $(this) ){
$( "#draggable" ).draggable( "disable" );
}
},
});
} );

When cloning a draggable object in jQuery UI, how can you transfer the data and events to the new element?

I have a draggable element with the helper: 'clone' set, but when it clones the element, none of the data() or events are persistent in the new element.
I've tried a number of ways to reattach the data(), but I can't seem to select the new element as well as the old element in the same statement.
For instance, I can select the initial element in the draggable stop() event:
$blah.draggable({
helper: 'clone',
stop: function(ev, ui) {
var oldData = $(ev.target).data('blah');
}
});
And I can also get the new element in the droppable drop() event:
$blah.droppable({
drop : function(ev, ui) {
var $newElement = ui.draggable;
}
});
But I can't figure out a way to get both in the same event.
What I'd like to do is somehow transfer the data, e.g.:
$newElement.data('blah', $oldElement.data('blah'));
Or otherwise make the data persistent, like you can with $blah.clone(true);
To get access to the data of original element in drop you can use ui.draggable.context. In the example below context would refer to the original dragged element and you have access to all of its content. Draggable refers to the new element that is being dropped.
$("#droppable").droppable({
drop: function(ev, ui) {
console.log(ui);
console.log(ui.draggable.context);
console.log($(ui.draggable.context).data('pic'));
}
});
I haven't worked too extensively with droppable, but couldn't you just do something like this?
$(".draggable").draggable({
helper: 'clone'
});
$("#droppable").droppable({
drop: function(ev, ui) {
$(this).append(ui.draggable.clone(true));
}
});
Seems to work unless there's something I'm missing:
http://jsfiddle.net/hasrq/
Turns out the problem was sortable, not draggable / droppable (I was attaching sortable later on, but figured it wasn't part of the problem here so I left it out of the original question).
I ended up using sort of a combination of #kingjiv's solution above, along with a not-the-greatest hack but at least it seems to be working:
$blah.sortable({
receive: function(ev, ui) {
// setting a global variable here
MyGlobals.cloneCache = ui.item.clone(true);
},
stop: function(ev, ui) {
$(ui.item).replaceWith(MyGlobals.cloneCache);
}
});
The idea is that you first clone the original item in the receive() event, cache this in a variable, and then replace the item with that in the stop() event.
Kind of ugly but at least it's working.
ui.item refers to dragged item. When the dragged item is cloned, there is no built-in way to access the target item from receive function. However, there is a bit hacky way how to do it:
$blah.sortable({
receive: function (ev, ui) {
var $target = $(this).data().sortable.currentItem;
var $source = $(ui.sender);
// now you can copy data from source to target
$target.data('data-item', $source.data('data-item'));
}
});

jquery droppable accept

Can anyone tell me how I can write a function in accept condition and then how does it finds out that what to accept and what not to accept.
For example, I want to accept div a and div b in accept condition. How can I write I through a function?
If you want the droppable to accept a selection of elements you can do something like this:
$(".droppable").droppable({
accept: function(d) {
if(d.hasClass("foo")||(d.attr("id")=="bar")){
return true;
}
}
});
This droppable will accept elements with the class "foo" or an element with the id "bar"
If I understand your question correctly, you want to conditionally accept the dropped element based on custom code. Aberon's answer is the typical case: you want to allow only certain draggable options to be dropped based on their class, and all others will revert. If that answers your question, then fine.
However, there is a case for having the revert animation happen conditionally based on something more complex than a class match. In my own project, I am using drag-drop to add users to a group. If the dropped user is already in the group, I want the user helper element to revert back. Otherwise, I go ahead with an AJAX action to insert them. This is no substitute for back-end checking, but it's nice visual feedback.
I have looked elsewhere for a simple answer to this. Note to JQuery maintainers: to me, the most straightforward way would be to add some property to the event object in the drop function, like this:
$('.target').droppable({
accept: '.valid'
drop: function(event, ui) {
if(isDropOK() == true) {
// add child here
} else {
event.revert = true;
}
}
});
Sadly that doesn't work. Here's what does "work", though: set the draggable element to always revert, and then hide the helper if the condition is met. You can get a reference to the helper by looking for the only element on the page that has the class ".ui-draggable-dragging". Here's an example, replace "isDropOK()" with your own logic:
$('.valid').draggable({
revert: true,
helper: 'clone',
opacity: 0.5
});
$('.target').droppable({
accept: '.valid'
drop: function(event, ui) {
if(isDropOK() == true) {
$('.ui-draggable-dragging').hide();
// add child here
}
}
});
So to recap, every element will always revert unless you step in on the drop event and manually hide the helper. The revert animation will still happen, but your users won't see it. It's a little hack, but the end result seems to work all right.
According to the Jquery documentation on Selectors.
All draggables that match the selector
will be accepted. If a function is
specified, the function will be called
for each draggable on the page (passed
as the first argument to the
function), to provide a custom filter.
The function should return true if the
draggable should be accepted.
Thus,
$('.selector').droppable({ accept: '.special' });
in their example will only act as if something has been dropped on it if it has the class 'special'. It looks like it can accept any Jquery selector.
In addition to Alex answer which is the best answer I found on this question, I like to add, that if you want to check for matching attributes between droppable and draggable, you can use the keyword this to access the droppable within your accept function. Here is a small example I recently used:
accept : function (draggable) {
var id_group_drop, id_group_drag;
//get the "id_group" stored in a data-attribute of the draggable
id_group_drag = $(draggable).attr("data-id-group");
//get the "id_group" stored in a data-attribute of the droppable
id_group_drop = $(this).parent().attr("data-id-group");
//compare the id_groups, return true if they match or false otherwise
return id_group_drop == id_group_drag;
}
So the draggable is only accepted, when it's id_group (remember, just an example) matches with the id_group of the droppable, otherwise the draggable would be reverted. I think this could be a very common use-case, maybe this will help someone.
This is the solution that I use:
... accept: '#diva,#divb', ...
I've figured out a solution that works based on the condition that no draggable should be placed in a droppable already occupied by another draggable:
$(".placement").droppable({
accept: function(elm) {
// only allow draggables to the placement if there's no other draggable
// in the droppable
if (!$(this).attr('isbusy'))
return true;
},
drop: function(event, ui) {
$(this).attr('isbusy', 'yeap'); // fill key with something
var draggable = $(ui.draggable[0]);
// free the draggable's previous droppable
if (draggable.attr('droppable')) {
$('#' + draggable.attr('droppable')).attr('isbusy', '');
}
// save the new draggable's droppable
draggable.attr('droppable', $(this).attr('id'));
},
});
Instead of using a class as accept, you can just use a function like
and return true if it matches your criteria
$('#mydroppable').droppable(
{
accept: function() { return true; },
drop: function () { alert("Dropped!"); }
});
This is my solution:
var foo = true;
$( ".draggable" ).draggable({
revert: function(){if(foo == true){return true;}else{foo = true;}},
});
$("#droppable").droppable({
activeClass: "ui-state-hover",
hoverClass: "ui-state-active",
drop: function( event, ui ) {
if($this == $that){
foo = true;
alert("this will revert the draggable back to original position");
}else{
foo = false;
alert("this will NOT revert the draggable back to original position");
}
}
});

Categories