javascript - 使用 HTML5 Drag'n'Drop 进行列表排序 - 根据鼠标拖放到上方或下方

标签 javascript html drag-and-drop

背景

我正在处理一个可排序的列表,以避免必须在数据库中手动输入排序顺序号。它通过 HTML5 的拖放功能工作,即 Javascript 中新的 drag* 事件。

我目前大部分时间都在使用它。我可以点击并拖动,它会自行排序。

问题

据我所知,drop 以及 dragstartdragend 事件只知道它们要去的元素进入。他们无法判断鼠标是在拖放区的上半部分还是下半部分。

我想要的是,当我将鼠标悬停在列表项的上半部分时,拖动的内容将放置在该项目的上方。然后,如果我将鼠标悬停在下半部分,则将拖动的内容放置在项目下方。

目前:

在下面的屏幕截图中,我展示了我的代码的一个工作(简化)示例。我在放置目标上使用 border-bottom 来表明它就是目标。请注意,当“Item 1”位于“Item 2”上方时,“Item 2”在底部会亮起,无论我将鼠标悬停在上半部分还是下半部分。

enter image description here

代码

var dragging = null;

document.addEventListener('dragstart', function(event) {
		dragging = event.target;
    event.dataTransfer.setData('text/html', dragging);
});

document.addEventListener('dragover', function(event) {
    event.preventDefault();
});

document.addEventListener('dragenter', function(event) {
    event.target.style['border-bottom'] = 'solid 4px blue';
});

document.addEventListener('dragleave', function(event) {
    event.target.style['border-bottom'] = '';
});

document.addEventListener('drop', function(event) {
    event.preventDefault();
    event.target.style['border-bottom'] = '';
    event.target.parentNode.insertBefore(dragging, event.target.nextSibling);
});
ul {
  margin:0;
  padding:0
}
li {
  cursor:move;
  display:block;
  padding:20px 10px;
  background:white;
  border-bottom:solid 1px gray;
}
<ul>
    <li draggable="true" class="sortable-bulk">List Item 1</li>
    <li draggable="true" class="sortable-bulk">List Item 2</li>
    <li draggable="true" class="sortable-bulk">List Item 3</li>
    <li draggable="true" class="sortable-bulk">List Item 4</li>
    <li draggable="true" class="sortable-bulk">List Item 5</li>
    <li draggable="true" class="sortable-bulk">List Item 6</li>
    <li draggable="true" class="sortable-bulk">List Item 7</li>
    <li draggable="true" class="sortable-bulk">List Item 8</li>
    <li draggable="true" class="sortable-bulk">List Item 9</li>
    <li draggable="true" class="sortable-bulk">List Item 10</li>
</ul>

问题

有没有办法让它落在上方或下方,而不总是下方,具体取决于拖动时鼠标的位置?

最佳答案

回答

所以在命运的转折中,对另一个问题的评论回复将我指向了这里的答案。 wolf-war值得称赞的是pointing me到正确的事件和方法。

无论如何,开始回答。解决方案在于使用 dragover 事件,而不是使用 dragenter 事件。 dragover 在您悬停时一直开火。

问题代码的代码更改

我们摆脱了 dragenter 代码:

document.addEventListener('dragenter', function(event) {
    event.target.style['border-bottom'] = 'solid 4px blue';
});

替换为:

document.addEventListener('dragover', function(event) {
    event.preventDefault();
    var bounding = event.target.getBoundingClientRect()
    var offset = bounding.y + (bounding.height/2);
    if ( event.clientY - offset > 0 ) {
        event.target.style['border-bottom'] = 'solid 4px blue';
        event.target.style['border-top'] = '';
    } else {
        event.target.style['border-top'] = 'solid 4px blue';
        event.target.style['border-bottom'] = '';
    }
});

然后在 drop 部分,我们检查放置目标的边框,并使用它在上方或下方插入拖动的内容。

完整的工作代码:

var dragging = null;

document.addEventListener('dragstart', function(event) {
    var target = getLI( event.target );
    dragging = target;
    event.dataTransfer.setData('text/plain', null);
    event.dataTransfer.setDragImage(self.dragging,0,0);
});

document.addEventListener('dragover', function(event) {
    event.preventDefault();
    var target = getLI( event.target );
    var bounding = target.getBoundingClientRect()
    var offset = bounding.y + (bounding.height/2);
    if ( event.clientY - offset > 0 ) {
       	target.style['border-bottom'] = 'solid 4px blue';
        target.style['border-top'] = '';
    } else {
        target.style['border-top'] = 'solid 4px blue';
        target.style['border-bottom'] = '';
    }
});

document.addEventListener('dragleave', function(event) {
    var target = getLI( event.target );
    target.style['border-bottom'] = '';
    target.style['border-top'] = '';
});

document.addEventListener('drop', function(event) {
    event.preventDefault();
    var target = getLI( event.target );
    if ( target.style['border-bottom'] !== '' ) {
        target.style['border-bottom'] = '';
        target.parentNode.insertBefore(dragging, event.target.nextSibling);
    } else {
        target.style['border-top'] = '';
        target.parentNode.insertBefore(dragging, event.target);
    }
});

function getLI( target ) {
    while ( target.nodeName.toLowerCase() != 'li' && target.nodeName.toLowerCase() != 'body' ) {
        target = target.parentNode;
    }
    if ( target.nodeName.toLowerCase() == 'body' ) {
        return false;
    } else {
        return target;
    }
}
ul {
  margin:0;
  padding:0
}
li {
  cursor:move;
  display:block;
  padding:20px 10px;
  background:white;
  border-bottom:solid 1px gray;
}
<ul>
    <li draggable="true">List Item 1</li>
    <li draggable="true">List Item 2</li>
    <li draggable="true">List Item 3</li>
    <li draggable="true">List Item 4</li>
    <li draggable="true">List Item 5</li>
    <li draggable="true">List Item 6</li>
    <li draggable="true">List Item 7</li>
    <li draggable="true">List Item 8</li>
    <li draggable="true">List Item 9</li>
    <li draggable="true">List Item 10</li>
</ul>

关于javascript - 使用 HTML5 Drag'n'Drop 进行列表排序 - 根据鼠标拖放到上方或下方,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44415228/

相关文章:

带有拖放功能的 jQuery 布局

javascript - 我的逻辑方向正确吗?

javascript - 如何构造嵌套的 Promise

javascript - Jquery/Javascript 中的 Post/Get 处理程序

javascript - html5 : is there a way to prevent children interfering with drag events

javascript - 使用 JavaScript 在 DataList 中搜索匹配项

javascript - Angular 2 - 在 iframe 上注入(inject)自定义 header

javascript - 使用 Javascript 与 HTML 来显示图像

.net - 在 C# 中的面板上拖放时移动控件

javascript - 无法捕获fabricjs中的拖动事件