Overview:
I have a page which uses jquery.event.drag and jquery.event.drop. I need to be able to drag and drop onto elements which are constantly being added to the dom, even after the drag has started.
Problem:
When the dragstart
event fires it checks for available drop targets and adds them to the drag object.
The problem I have is I am adding drop targets dynamically, after the dragstart
event has fired, and therefore the user cannot drop onto these dynamically added drop targets.
Example:
http://jsfiddle.net/blowsie/36AJq/
Question:
How can I update the drag to allow dropping on elements which have been added to the dom after drag has started?
Additional properties or methods can be added to jQuery.event.special.drag.callback.prototype and will be available in all event handlers for all elements. The following examples are intended to show how the drag special events can be configured to acheive a wide range of features for any drag interaction.
The dragstart event is fired when the user starts dragging an element or text selection. Initiate the drag-and-drop operation. See the drag event for example code or this JSFiddle demo.
The dragstart event is fired when the user starts dragging an element or text selection. Initiate the drag-and-drop operation. See the drag event for example code or this JSFiddle demo. The compatibility table on this page is generated from structured data.
.drag( handler, options ) Binds a "drag" event handler to each element in the jQuery collection. Equivilent to: .bind("drag", options, handler ) .drag( type, handler, options ) Binds a "dragtype" event handler to each element in the jQuery collection. (Types are: init, start, end) Equivilent to: .bind("dragtype",...
You can use this snippet.
The important function is: $.event.special.drop.locate();
Tested on chrome/safari/firefox/ie9 and seems to work.
SEE DEMO
UPDATE
For overlapping events, see if following code works. I set it inside an anonymous function just to avoid any global variable. Idea is to use currentTarget property of event to check if not the same element is triggering same event. I set an id on newdrop element just in purpose of test here.
SEE UPDATED DEMO
(function () {
var $body = $("body"),
newdrops = [],
currentTarget = {},
ondragstart = function () {
$(this).css('opacity', .75);
}, ondrag = function (ev, dd) {
$(this).css({
top: dd.offsetY,
left: dd.offsetX
});
}, ondragend = function () {
$(this).css('opacity', '');
for (var i = 0, z = newdrops.length; i < z; i++)
$(newdrops[i]).off('dropstart drop dropend').removeClass('tempdrop');
newdrops = [];
}, ondropstart = function (e) {
if (currentTarget.dropstart === e.currentTarget) return;
currentTarget.dropstart = e.currentTarget;
currentTarget.dropend = null;
console.log('start::' + e.currentTarget.id)
$(this).addClass("active");
}, ondrop = function () {
$(this).toggleClass("dropped");
}, ondropend = function (e) {
if (currentTarget.dropend === e.currentTarget) return;
currentTarget.dropend = e.currentTarget;
currentTarget.dropstart = null;
console.log('end::' + e.currentTarget.id)
$(this).removeClass("active");
};
$body.on("dragstart", ".drag", ondragstart)
.on("drag", ".drag", ondrag)
.on("dragend", ".drag", ondragend)
.on("dropstart", ".drop", ondropstart)
.on("drop", ".drop", ondrop)
.on("dropend", ".drop", ondropend);
var cnt = 0;
setInterval(function () {
var dataDroppables = $body.data('dragdata')['interactions'] ? $body.data('dragdata')['interactions'][0]['droppable'] : [];
var $newDrop = $('<div class="drop tempdrop" id="' + cnt + '">Drop</div>');
cnt++;
$("#dropWrap").append($newDrop);
var offset = $newDrop.offset();
var dropdata = {
active: [],
anyactive: 0,
elem: $newDrop[0],
index: $('.drop').length,
location: {
bottom: offset.top + $newDrop.height(),
elem: $newDrop[0],
height: $newDrop.height(),
left: offset.left,
right: offset.left + $newDrop.width,
top: offset.top,
width: $newDrop.width
},
related: 0,
winner: 0
};
$newDrop.data('dropdata', dropdata);
dataDroppables.push($newDrop[0]);
$newDrop.on("dropstart", ondropstart)
.on("drop", ondrop)
.on("dropend", ondropend);
$.event.special.drop.locate($newDrop[0], dropdata.index);
newdrops.push($newDrop[0]);
}, 1000);
})();
I wasn't able to get this working using jquery.event.drag and jquery.event.drop, but I did make it work with the native HTML5 events:
http://jsfiddle.net/R2B8V/1/
The solution was to bind the events on the drop targets within a function and call that to update the bindings. I suspect you could get this working with jquery.event.drag and jquery.event.drop using a similar principal. If I can get those working I will update my answer.
Here is the JS:
$(function() {
var bind_targets = function() {
$(".drop").on({
dragenter: function() {
$(this).addClass("active");
return true;
},
dragleave: function() {
$(this).removeClass("active");
},
drop: function() {
$(this).toggleClass("dropped");
}
});
};
$("div[draggable]").on({
dragstart: function(evt) {
evt.originalEvent.dataTransfer.setData('Text', 'data');
},
dragend: function(evt) {
$('.active.drop').removeClass('active');
}
});
setInterval(function () {
$("#dropWrap").append('<div class="drop">Drop</div>');
// Do something here to update the dd.available
bind_targets();
}, 1000)
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With