I am trying to drop an element on top of multiple droppable DIVs, but I have a problem with event bubbling. The droppable event gets executed multiple times, based on how many elements are layered on top of each other. I would like to execute it only for the last/top element.
Please see this jsFiddle.
HTML:
<div id="1" class="box droppable">1
<div id="2" class="box droppable">2
<div id="3" class="box droppable">3</div>
</div>
</div>
<div id="drag" class="draggable">Drag me! I dare you!</div>
<div id="output"></div>
jQuery:
$(function () {
$(".draggable").draggable({
revert: "invalid"
});
$(".droppable").droppable({
tolerance: 'pointer',
drop: function (event, ui) {
content = $('#output').html();
$('#output').html(content + "Dropped "+ui.draggable.prop('id') + " on " +$(this).attr('id')+".<br/>");
}
});
});
When you drop the draggable on element 1 it will say that you dropped it on 1. That's correct. If you drop it on element 3, it will say you dropped it on 1,2,3. I need to limit this only to the third/top-most element. How can I achieve this please?
I've experienced this in the past in a different situation, where it required a confirmation, so the droppable target ID could be stored in an invisible field (the last value), but I need to make it directly this time, no workarounds :(
Thanks!
If I only weren't so lazy to browse around the documentation for a bit. The solution to this problem can be found here. By specifying the greedy option user can limit the affected droppable elements.
In other words
$(".droppable").droppable({
tolerance: 'pointer',
greedy: true, //limit to only top-most droppable element
drop: function (event, ui) {
content = $('#output').html();
$('#output').html(content + "Dropped "+ui.draggable.prop('id') + " on " +$(this).attr('id')+".<br/>");
}
});
Related
Im using jquery-UI droppable, When I drag the small green box over the blue ones from top to bottom everything works as I want but when I drag the box from bot to top the parent box trigger the over event and gets highlighted, i want that just one element being highligthed at same time, one of the siblings or the parent.
HTML:
<div class="box">Drag me</div>
<div class="drop">Drophere
<div class="drop">Drophere</div>
<div class="drop">Drophere</div>
<div class="drop">Drophere</div>
</div>
JS:
$(".box").draggable();
$(".drop").droppable({
accept: ".box",
hoverClass: "drophere",
greedy:true
});
http://jsfiddle.net/Deivid11/44bg1bz4/4/
You can check on over event if a child .drop has class .drophere and if so, remove .drophere from the parent. Like this:
over: function (e, ui) {
$('.drop .drophere').parent().removeClass('drophere');
}
http://jsfiddle.net/p1wjk56h/1/
I have a page with two DIVs, both of which are droppable. One of them (#droppable2) is initially hidden and is only shown when I hover the draggable item over the first DIV #droppable (with the dropover event). Example see here: http://codepen.io/anon/pen/AdLJr
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.0/jquery-ui.js"></script>
<div id="droppable">Drop here 1</div>
<div id="droppable2" style="top: 300px; display: none;">Drop here 2</div>
<div id="draggable">Drag me</div>
<div id="log"></div>
JS:
$( "#draggable" ).draggable();
$( "#droppable,#droppable2" ).droppable({
drop: function() {
$('#log').html($('#log').html() + '<br>DROP');
},
over: function() {
$('#log').html($('#log').html() + '<br>over');
$('#droppable2').show();
}
});
However, when I now hover the draggable item over the second DIV, its dropover event is not fired like it should. Only when drop the draggable item and then pick it up again does it seem to work. The drop event already works on the first try though.
Any ideas what's causing this? I assume this might be a bug?
Ok here is an actual answer. Change your first JS line to this:
$( "#draggable" ).draggable({ refreshPositions: true });
From the jQuery UI draggable docs for the refreshPositions option:
If set to true, all droppable positions are calculated on every mousemove.
Caution: This solves issues on highly dynamic pages,
but dramatically decreases performance.
I also did it in that way with this line $( "#draggable" ).draggable({ refreshPositions: true });, but using a class draggable, but only works for once. I can only drag and drop for once one element. And if there is another element in the place where a droped they all don't work
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)
I am using the JQuery UI droppable library features, and I want to provide visual user feedback when they hover over a droppable target. For this I can easily use the hoverClass option to specify which class to use when they have a draggable item hovering over.
But what I want to do is use a different hoverClass value depending on some logic. Basically, there are a number of areas that are "droppable", and there is a number of items that can be dragged and dropped - however, not all items can be dropped on all areas. So I would like to have, for example, a green background if the drop is valid, and a red background if the drop is invalid.
How can this be done? I know what logic I want to use, but where can I add the logic. It obviously needs to be somewhere I can access the element being dragged, and the potential drop target element.
My simple code so far is as follows:
$(".DragItem").draggable({
revert: true,
helper: "clone"
});
$(".DropItem").droppable({
tolerance: "touch",
hoverClass: "DropTargetValid"
});
$(".DropItem").droppable({
tolerance: "touch",
hoverClass: "DropTargetValid",
over: function(event, ui) {
console.log(ui.draggable); // the draggable object
console.log($(this)); // the droppable object
}
});
This should do it. On over that event will be triggered on all .DropItem elements. You can find more about the available events API here: http://api.jqueryui.com/droppable/
I think your issue is trying to do it with the class itself, when droppable has its own hover event, called over jQuery droppable API #over
So you'd want:
$(".DropItem").droppable({
tolerance: "touch",
over: function(event, ui) {
// ... logic goes here
}
});
The other answers are exactly what I was looking for. However, I want to go into a bit further detail here to give a better example of how to process the logic.
Lets say for example, with have some simple HTML as follows. This HTML basically has 4 draggable objects, and 4 possible drop targets:
<div style="margin-bottom:20px;">
<div data-id="1" class="DragItem">I am 1</div>
<div data-id="2" class="DragItem">I am 2</div>
<div data-id="3" class="DragItem">I am 3</div>
<div data-id="4" class="DragItem">I am 4</div>
</div>
<div>
<div data-id="123" class="DropItem">I accept 1, 2 and 3</div>
<div data-id="23" class="DropItem">I accept 2 and 3</div>
<div data-id="34" class="DropItem">I accept 3 and 4</div>
<div data-id="1234" class="DropItem">I accept all</div>
</div>
As can be seen, I have used data-* attributes to store specific identifying values. The IDs on the DragItem identify the drag object, and the IDs on the DropItem contain all valid values.
The javascript that processes this logic, and then applies the correct classes is as follows:
$(".DragItem").draggable({
revert: true,
helper: "clone"
});
$(".DropItem").droppable({
tolerance: "touch",
over: function (event, ui) {
var dropItem = $(this);
var dragItem = $(ui.draggable);
var valid = String(dropItem.data("id")).indexOf(dragItem.data("id")) > -1;
if (valid) {
dropItem.addClass("DropTargetValid");
} else {
dropItem.addClass("DropTargetInvalid");
}
},
out: function (event, ui) {
var dropItem = $(this);
dropItem.removeClass("DropTargetValid");
dropItem.removeClass("DropTargetInvalid");
},
deactivate: function (event, ui) {
var dropItem = $(this);
dropItem.removeClass("DropTargetValid");
dropItem.removeClass("DropTargetInvalid");
}
});
As can be seen, I am doing a simple "does string contain" logic check. This is fine for small numbers but if there is ever a need to do more than 9 object we would need a more reliable string in the DropItem data-id value.
I also use the out and deactivate events to clear up the applied classes. In this example I duplicate the code, but this could easily be replaced with a single function used by both events.
Finally, the moment you have all been waiting for, here is a working example.
Use the Draggable Start/Stop events to add and remove classes to your dropzones.
http://api.jqueryui.com/draggable/#event-start
I have a list of draggable items, and I wish to be able to drag them onto a sortable content block. Here’s my markup:
<div class='items'>
<div class="one">One</div>
<div class="two">Two</div>
</div>
<div class="content">
<div class="block">Foo</div>
<div class="block">Bar</div>
</div>
The thing is, that I want the dragged item to "become" a block as soon as the dragging starts and remain a block when it’s dropped. I have tried this:
$('.items div').draggable({
helper: function(e) {
return $('<div>').addClass('block').text( $(e.target).text() );
},
connectToSortable: ".content"
});
$('.content').sortable();
Fiddle: http://jsfiddle.net/MF4qu/
But even if I create a custom helper that looks like a block, it reverts back to the original dragged element as soon as it’s dropped. Does anyone know how to properly insert a block in my example page? I already looked through the UI API but I can’t figure it out.
When the draggable element is dropped into sortable, it triggers the sortable update event. One solution is to listen to that event, and turn the dropped item into a block:
$('.items div').draggable({
helper: function(e) {
return $('<div>').addClass('block').text( $(e.target).text() );
},
connectToSortable: ".content"
});
$('.content').sortable({
update: function (event, ui) {
ui.item.addClass('block');
}
});
Working demo: http://jsfiddle.net/DaXuT/1/