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

Posted

技术标签:

【中文标题】使用 HTML5 Drag\'n\'Drop 进行列表排序 - 根据鼠标放置在上方或下方【英文标题】:List Sorting with HTML5 Drag'n'Drop - Drop Above or Below Depending on Mouse使用 HTML5 Drag'n'Drop 进行列表排序 - 根据鼠标放置在上方或下方 【发布时间】:2017-11-08 23:01:11 【问题描述】:

背景

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

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

问题

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

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

目前:

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

代码

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>

问题

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

【问题讨论】:

【参考方案1】:

回答

因此,命运的转折,对另一个问题的评论回复将我指向了这里的答案。 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>

【讨论】:

当你点击它时是否可以做到这一点:光标仍然出现,而不是箭头指针?我正在尝试做一些简单的事情,比如 jQuery UI 版本,但只有光标:move jqueryui.com/sortable/#default

以上是关于使用 HTML5 Drag'n'Drop 进行列表排序 - 根据鼠标放置在上方或下方的主要内容,如果未能解决你的问题,请参考以下文章

HTML5拖放(Drag和Drop)元素使用详解

HTML5 Drag & Drop 在拖动时更改光标(不要使用 UI)

HTML5 drop 事件子阻止

HTML5: dragover(), drop(): 如何获取当前 x,y 坐标?

HTML5 drop 事件没有被触发。

原生 drag drop HTML5