在 jquery.event.drag 中触发 dragstart 后更新可用的放置目标

Posted

技术标签:

【中文标题】在 jquery.event.drag 中触发 dragstart 后更新可用的放置目标【英文标题】:Update available drop targets after dragstart fires in jquery.event.drag 【发布时间】:2013-04-03 14:15:30 【问题描述】:

概述:

我有一个使用jquery.event.drag 和jquery.event.drop 的页面。 我需要能够拖放到不断添加到 dom 的元素上,即使在拖动开始之后也是如此。


问题:

dragstart 事件触发时,它会检查可用的放置目标并将它们添加到拖动对象。

我遇到的问题是我在dragstart 事件触发后动态添加放置目标,因此用户无法放置到这些动态添加的放置目标上。


示例:

http://jsfiddle.net/blowsie/36AJq/


问题:

如何更新拖动以允许拖放开始后已添加到 dom 的元素?

【问题讨论】:

【参考方案1】:

你可以使用这个sn-p。

重要的功能是:$.event.special.drop.locate();

在 chrome/safari/firefox/ie9 上测试,似乎可以工作。

SEE DEMO


更新

对于重叠事件,请查看以下代码是否有效。我将它设置在一个匿名函数中只是为了避免任何全局变量。 想法是使用 event 的 currentTarget 属性来检查是否不是同一个元素触发了同一个事件。我在 newdrop 元素上设置了一个 id 只是为了在这里进行测试。

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);
)();

【讨论】:

还有任何想法如何阻止拖动事件重叠?请参阅jsfiddle.net/blowsie/qtnQF/2上的控制台 您应该能够在 drop.js 中使用容差停止重叠播放。不确定我没有足够的时间玩,但似乎是您正在寻找的东西。另一种更简单的方法是使用各个事件的 e.currentTarget 来检查是否不是同一个先前元素触发了事件。我会尽快更新我的答案。【参考方案2】:

我无法使用 jquery.event.drag 和 jquery.event.drop 让它工作,但我确实让它与原生 html5 事件一起工作:

http://jsfiddle.net/R2B8V/1/

解决方案是在函数内绑定放置目标上的事件并调用它来更新绑定。我怀疑您可以使用类似的主体与 jquery.event.drag 和 jquery.event.drop 一起使用。如果我能让那些工作,我会更新我的答案。

这里是 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)
);

【讨论】:

这有两个问题:浏览器可比性和自定义。 (例如,.dragdragEnd 上的光标位置和位置。) 我并没有说这是一个完美的解决方案。我在说明一个概念。 你提到的自定义可以在这里实现。关于浏览器兼容性,这将适用于绝大多数浏览器。它在 IE 8 或更低版本中不起作用,但这不一定是问题。 看起来@roasted 刚刚发布了您正在寻找的解决方案【参考方案3】:

你不能。在dragstart 上,可能的拖放区是从 DOM 计算出来的,直到dragend 才能编辑。即使不断地重新绑定.on()(演示:http://jsfiddle.net/36AJq/84/)也不会提供预期的效果。

我以不同的方式解决了这个问题。 (演示:http://jsfiddle.net/36AJq/87/)

    从 HTML 中的每个 &lt;div&gt; 开始。 应用opacity: 0 使其不可见,并应用width: 0 以防止它在隐藏时获得dropend。 使用 setInterval 每 1000 毫秒显示下一个隐藏的 div ($('.drop:not(.visible)').first())。

JS:

$("body")
  .on("dragstart", ".drag", function () 
    $(this).css('opacity', .75);
  )
  .on("drag", ".drag", function (ev, dd) 
    $(this).css(
      top: dd.offsetY,
      left: dd.offsetX
    );
  )
  .on("dragend", ".drag", function () 
    $(this).css('opacity', '');
  )
  .on("dropstart", ".drop", function () 
    $(this).addClass("active");
  )
  .on("drop", ".drop", function () 
    $(this).toggleClass("dropped");
  )
  .on("dropend", ".drop", function () 
    $(this).removeClass("active");
  );
setInterval(function () 
    $('.drop:not(.visible)').first()
      .addClass('visible').removeClass('hidden');
, 1000)

【讨论】:

投反对票的人能否告诉我他们为什么投反对票? 我不是反对者,但由于您对我的解决方案不够完美,所以我会指出您的问题。它要求在拖动开始时创建所有放置目标,此时问题的最重要部分是需要动态添加放置目标。 元素从 dom 中动态添加和删除是有原因的,为了减小 dom 大小并提高性能,仅供参考,我将其应用于 js 网格,行数范围可以从 1 到10000,但由于我使用网格,在任何给定时间页面上只会有大约 50 个【参考方案4】:

启用refreshPositions option。

【讨论】:

OP 没有使用 jQueryUI。 同样refreshPositions也没有解决JQUI中的问题bugs.jqueryui.com/ticket/7619【参考方案5】:

为什么不将所有 div 放入页面中并将其可见性设置为隐藏?然后使用 setInterval() 每秒更改每个人的可见性。

【讨论】:

出于某种原因动态添加和删除 dom 中的元素,以减小 dom 大小并提高性能 @Blowsie 如果将它们的宽度/高度设置为 0,然后每秒将其增加到所需的大小会怎样。 它们仍然在 dom 中并且速度很慢,然后调整它们的大小会导致回流。它真的不是一个可行的选择

以上是关于在 jquery.event.drag 中触发 dragstart 后更新可用的放置目标的主要内容,如果未能解决你的问题,请参考以下文章

jquery.event.drag 和 jquery 可调整大小不兼容

D触发器深入详细介绍(zhuanzai)

D触发器构成十进制计数器原理

oracle update触发器如何获取被修改的字段

onpageshowonpagehideonloadonunload

D触发器