dragend, dragenter, and dragleave firing off immed

2019-02-03 01:56发布

问题:

I'm trying to make a list of elements which can be repositioned by dragging and dropping. The first element, Box 1, works just fine 100% of the time. Sometimes the second box works, but none of the others work as expected. They seem to fire off all the drag events at once as soon as you start dragging them.

I'm using the latest Chrome (v23) if that matters.

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');
}

Here's the 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>

Here is a jsFiddle of my code: http://jsfiddle.net/zGSpP/5/

回答1:

Had this problem just now - it is a Chrome bug, you must not manipulate the DOM in dragStart event, or else dragEnd fires immediately. The solution for me turned out to be - use setTimeout() and manipulate the DOM inside the function you timeout.



回答2:

It seems to be a Chrome bug : https://groups.google.com/a/chromium.org/forum/?fromgroups=#!msg/chromium-bugs/YHs3orFC8Dc/ryT25b7J-NwJ



回答3:

Not sure whether it is a bug or not, but I think the problem comes in when you change the DOM in the dragstart handler. The reason the first box works I presume has something to do with the fact that its position is before the other boxes in the DOM and when you change the DOM, the position (?) of the first one isn't affected and the dragdrop events fire reliably.

You will need to refactor your code somewhat, and add the dropSpaces in the dragenter event handler. You would need to change your addDropSpaces method to accommodate the fact that it will be called multiple times, though.



回答4:

Life is easier with jQuery. This is exactly what you described you wanted, using jQueryUI.

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

which allows sorting. The code:

<!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> 

also see: http://jqueryui.com/sortable/

Try adding the events manually, the long way, something like :

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

etc. just to see the effect?



回答5:

change this function

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

in your version you are adding that div after each of the $questionItems.



回答6:

You need to prevent the "event" from bubbling up so it doesn't fire up other events. See https://developer.mozilla.org/en-US/docs/DOM/event.stopPropagation

For startDrag, toggleDropStyles, removeDropSpaces functions, you'd write something like this:

function startDrag(ev) {
   ev.stopPropagation();
   ... your code here ...
}