Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update available drop targets after dragstart fires in jquery.event.drag

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?

like image 471
Blowsie Avatar asked Apr 11 '13 11:04

Blowsie


People also ask

What can be added to jQuery drag special events?

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.

When is the dragstart event fired?

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.

What is dragstart event in JSFiddle?

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.

What does drag () do in jQuery?

.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",...


2 Answers

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);
})();
like image 81
A. Wolff Avatar answered Oct 07 '22 00:10

A. Wolff


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)
});
like image 34
Ali Gangji Avatar answered Oct 07 '22 01:10

Ali Gangji