拖动时dragend、dragenter和dragleave立即触发,是什么原因?

31

我正在尝试制作一个可以通过拖放重新定位的元素列表。第一个元素Box 1始终可以正常工作,但有时第二个框也能正常工作,而其他框则无法按预期工作。它们似乎会在您开始拖动它们时一次性触发所有的拖动事件。

如果有影响的话,我正在使用最新版本的Chrome(v23)。

var $questionItems = $('.question-item');

$questionItems
  .on('dragstart', startDrag)
  .on('dragend', removeDropSpaces)
  .on('dragenter', toggleDropStyles)
  .on('dragleave', toggleDropStyles);


function startDrag(){
  console.log('dragging...');
  addDropSpaces();
}

function toggleDropStyles(){
  $(this).toggleClass('drag-over');
  console.log(this);
}


function addDropSpaces(){
  $questionItems.after('<div class="empty-drop-target"></div>');
  console.log('drop spaces added');
}

function removeDropSpaces(){
  $('.empty-drop-target').remove();
  console.log('drop spaces removed');
}

这是 HTML 代码:

<div class="question-item" draggable="true">Box 1: Milk was a bad choice.</div>
<div class="question-item" draggable="true">Box 2: I'm Ron Burgundy?</div>
<div class="question-item" draggable="true">Box 3: You ate the entire wheel of cheese?</div>
<div class="question-item" draggable="true">Box 4: Scotch scotch scotch</div>

这是我的代码的 jsFiddle 链接:http://jsfiddle.net/zGSpP/5/


你是否正在使用jQuery的可拖动插件?你是在哪里通过element.draggable();初始化元素为可拖动的呢? - jbabey
这真是奇怪。你应该使用JQueryUI小部件事件作为解决方法:http://jqueryui.com/draggable/ - sdespont
jbabey: HTML5属性'draggable=true' - Bill
6个回答

40

我刚刚遇到了这个问题——这是一个Chrome的bug,你不可以在dragStart事件中操作DOM,否则dragEnd会立即触发。对我来说解决方案是——使用setTimeout()函数,并在超时后的函数中操作DOM。


这对我有用。另外,我在一个页面变体上也使用了相同的代码,但在另一个页面上却没有,它们之间唯一的区别是在失败时被操作元素的父元素未呈现(被隐藏)。 - Anton Bielousov
通常情况下都能正常工作,但有时必须像这个答案一样操作(这对我有效)。我不知道出了什么问题... - JaeIL Ryu
对于现代Web框架(如Angular、Vue和React)的用户:还要考虑数据绑定机制,这可能会使这些DOM操作更难以检测。 - David Brem

12

2
这个 bug 在 Chrome 32 中仍然存在。 - Akkuma
2
在Chrome 47中仍然似乎存在这个问题。 - Puppy
1
仍然存在于Chrome 55.0.2883.87版本中。 - Able Johnson
3
仍然在Chrome 62中:/ - cliff.meyers
1
仍然在Chrome 101版本中 - LuisAFK
显示剩余2条评论

9
我不确定这是否是一个错误,但我认为问题在于您在dragstart处理程序中更改DOM。我猜第一个框正常工作的原因与其位置在DOM中的其他框之前有关,当您更改DOM时,第一个框的位置(?)不受影响,因此可以可靠地触发拖放事件。
您需要进行一些代码重构,并在dragenter事件处理程序中添加dropSpaces。您需要更改addDropSpaces方法以适应其将被多次调用的事实。

1

有了jQuery,生活变得更加轻松。这正是您所描述的,使用jQueryUI实现。

http://jqueryui.com/draggable/#sortable

它允许排序。代码:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>jQuery UI Draggable + Sortable</title>
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
    <link rel="stylesheet" href="/resources/demos/style.css" />
    <style>
    ul { list-style-type: none; margin: 0; padding: 0; margin-bottom: 10px; }
    li { margin: 5px; padding: 5px; width: 150px; }
    </style>
    <script>
    $(function() {
        $( "#sortable" ).sortable({
            revert: true
        });
        $( "#draggable" ).draggable({
            connectToSortable: "#sortable",
            helper: "clone",
            revert: "invalid"
        });
        $( "ul, li" ).disableSelection();
    });
    </script>
</head>
<body> 
<ul>
    <li id="draggable" class="ui-state-highlight">Drag me down</li>
</ul>

<ul id="sortable">
    <li class="ui-state-default">Item 1</li>
    <li class="ui-state-default">Item 2</li>
    <li class="ui-state-default">Item 3</li>
    <li class="ui-state-default">Item 4</li>
    <li class="ui-state-default">Item 5</li>
</ul>

</body>
</html> 

另请参阅:http://jqueryui.com/sortable/

尝试手动添加事件,以长方式进行,例如:

$questionItems[0].on('dragstart', startDrag); 
$questionItems[0].on('dragend', removeDropSpaces) 
$questionItems[0].on('dragenter', toggleDropStyles) 
$questionItems[0].on('dragleave', toggleDropStyles); 
$questionItems[1].on('dragstart', startDrag); 
$questionItems[1].on('dragend', removeDropSpaces) 
$questionItems[1].on('dragenter', toggleDropStyles)     
$questionItems[1].on('dragleave', toggleDropStyles); 

等等,只是为了看看效果吗?


我真的需要能够做更多的事情,而不仅仅是对列表进行排序,例如当一个项目被拖到另一个项目上方时它们变成一组。 - Bill
你仍然可以通过jQuery来处理事件。$( ".selector" ).on( "drag", function( event, ui ) {} ); 和 $( ".selector" ).on( "dragstart", function( event, ui ) {} ); 它还具有回调函数,因此您仍然可以使用已构建的函数。 - SoluableNonagon
另外,您是否尝试手动将每个.on()添加到每个单独的框项中?只是为了看看效果? - SoluableNonagon
类似于 $questionItems[0].on('dragstart', startDrag); $questionItems[0].on('dragend', removeDropSpaces) $questionItems[0].on('dragenter', toggleDropStyles) $questionItems[0].on('dragleave', toggleDropStyles); $questionItems[1].on('dragstart', startDrag); $questionItems[1].on('dragend', removeDropSpaces) $questionItems[1].on('dragenter', toggleDropStyles) $questionItems[1].on('dragleave', toggleDropStyles); 等等,只是为了看看效果? - SoluableNonagon
是的,我将尝试使用jQ UI的sortable。此时此刻,我只想让它正常工作。知道我也可以利用这些事件真是太好了。谢谢。 - Bill

0

似乎会阻止函数的其余部分执行?http://jsfiddle.net/zGSpP/11/ - Bill
这对我有用!谢谢你。 - undefined

-1

修改这个函数

function addDropSpaces()
{
  $(this).after('<div class="empty-drop-target"></div>');
}

在你的版本中,你正在每个$questionItems之后添加那个div。


目标是在每个框后面添加空格 - sdespont
这似乎与 bug 有关,但我确实希望在每个 .question-item 后注入 div,而不仅仅是当前的一个。 - Bill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接