如何使用纯JavaScript实现拖放功能?

7

我使用原生javascript编写了一个拖放代码。当我选择第一个div时,尝试移动到下一个位置时它会自动移动到最后一个位置。

我不想使用jquery库。

我的解决方法是创建一个主div作为容器,这样在容器内部可以轻松移动每个div。

I have attached what I tried, but I don't understand why this is happening.

const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.mainContainer')

draggables.forEach(draggable => {
  draggable.addEventListener('dragstart', () => {
    draggable.classList.add('dragging')
  })

  draggable.addEventListener('dragend', () => {
    draggable.classList.remove('dragging')
  })
})

containers.forEach(container => {
  container.addEventListener('dragover', e => {
    e.preventDefault()
    const afterElement = getDragAfterElement(container, e.clientY)
    const draggable = document.querySelector('.dragging')
    if (afterElement == null) {
      container.appendChild(draggable)
    } else {
      container.insertBefore(draggable, afterElement)
    }
  })
})

function getDragAfterElement(container, y) {
  const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]

  return draggableElements.reduce((closest, child) => {
    const box = child.getBoundingClientRect()
    const offset = y - box.top - box.height / 2
    if (offset < 0 && offset > closest.offset) {
      return { offset: offset, element: child }
    } else {
      return closest
    }
  }, { offset: Number.NEGATIVE_INFINITY }).element
}
body {
  font-family: sans-serif;
}

.mainContainer {
  padding: 6px 0;
  width: 100%;
}

.draggable {
  cursor: move;

  display: inline-block;
  margin: 0 6px;
  z-index: 1;

  color: #fff;
  background-color: #087ee5;
  border: 1px solid #0675d6;
  border-radius: 4px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
  text-align: center;
  max-width: 300px;
  min-width: 30px;
  border-collapse: separate;
  vertical-align: middle;
  padding-left: 7px;
  padding-right: 7px;
  padding-top: 10px;
  padding-bottom: 10px;
}

.draggable.dragging {
  opacity: 0.5;
}
<div class="mainContainer">
      <p class="draggable" draggable="true">MOVE 1</p>
      <p class="draggable" draggable="true">MOVE 2</p>
      <p class="draggable" draggable="true">MOVE 3</p>
      <p class="draggable" draggable="true">MOVE 4</p>
    </div>


1
看起来在计算最接近的元素时,您使用的是y位置而不是x位置,但是当我运行您的片段时,元素是水平(x)排列的。这可能是错误的根源吗?即使不是,我仍然怀疑计算中存在问题。您可以添加低级别测试或日志以确定决策出现错误的位置。 - Will
4个回答

2

在鼠标悬停时为可拖动元素添加事件监听器。 检查可拖动元素是否已经在手中。 如果没有,则将可拖动元素的节点添加到鼠标悬停后的节点之后。

将拖动的元素位置更改为绝对定位,并通过top和left定义其位置x和y。


0

如果您的列表仅限于一行,则添加X轴计算可以解决问题。

只需将 e.clientY 更改为 e.clientX 即可。

const afterElement = getDragAfterElement(container, e.clientX)

使用左坐标和宽度而不是高度和顶部

offset = x - box.left - box.width / 2;

0
我建议不要使用拖动事件,而是使用鼠标事件。我使用鼠标事件的效果和可靠性要好得多,而且你也可以在屏幕外拖动。
handleDragStart = () => {
  // Once mouse down is detected, you THEN start up the window listeners
  // You also append something to the body of your DOM to escape
  // your drag graphic from getting covered
  window.addEventListener('mousemove', handleDragItem);
  // Use mouse up to see if you moused up on something you were targetting
  // You can use mouse over events on your targets to have easy state tracking
  // No matter what, when window mouse up happens, you clean up all 
  // event listeners involved
  window.addEventListener('mouseup', handleStopDrag);
});

拖放涉及的每个元素都可以与起始窗口事件配对使用这些事件(JSX 的一种方式来看待它以节省一些打字)。

<div 
  onMouseDown={this.handleDragStart(index, width, height)}
  onMouseMove={this.handleDragEnter(index)}
  onMouseUp={this.handleDrop(index)}
  onMouseOut={this.handleDragOut(index)}
/>

0

如果您的问题是停止拖动并将您正在拖动的对象放置到指定位置,我建议您定义“区域”,在这些区域中您可以放置元素。这些区域取决于您有多少个元素,在这种情况下(4个),您可以使用相同的区域

1个区域将与拖动元素相同 3个区域位于其他元素之间+起始和结束空间

因此,您需要计算何时放置该元素,我还会使用事件mousedownmouseup,如果您想知道元素是否正在“拖动”,则可以检查元素是否为mousedown+mousemove

在我的情况下,我以前做过类似的解决方案,并制作了这些“区域”,使每个元素的一半成为一个区域,这样它们之间就可以有一些空间,最左边和最右边可以是< first_element.position.X> last_element.position.X


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