【问题标题】:Issue with Drag and Drop API or?拖放 API 的问题还是?
【发布时间】:2017-07-06 03:54:35
【问题描述】:

我遇到了 HTML5 拖放 API 的问题。

长话短说,除了dragstartdragoverdrop 之外,还有三个事件对我的目标至关重要。这三个事件是mousedownmouseupmouseleave

 

鼠标按下

当用户在给定元素上触发mousedown 事件时,以下函数将属性和侦听器应用于自身和目标元素。

function doThis() {
  d.setAttribute('draggable', true)
  d.addEventListener('dragstart', dragStart)
  t.style.background = 'black'
  t.addEventListener('dragover', dragOver)
  t.addEventListener('drop', drop)
}

 

onmouseup 和 mouseleave

当用户触发mouseupmouseleave 事件时,这些属性和侦听器将被移除。

function thenThis(){
  d.setAttribute('draggable', false)
  d.removeEventListener('dragstart', dragStart)
  t.style.background = 'none'
  t.removeEventListener('dragover', dragOver)
  t.removeEventListener('drop', drop)
}

我不得不添加mouseleave 监听器,因为mouseup 事件必须发生在它所绑定的元素上才能被触发。在不应用mouseleave 事件的情况下,用户可以通过双击元素并滑动将光标从元素上移开,直到超出元素的边界才松开鼠标按钮。


问题...

如果用户单击的可拖动元素太靠近其边缘,则尽管触发了mousedown 事件,该元素仍将变为不可拖动。

Here is an codepen MVC which can also be viewed below.

为什么会发生这种情况? mousedown 甚至被触发,但该元素仍然不可拖动...

有人知道我该如何解决这个问题或从一开始就阻止它发生吗?

var d = document.getElementById('draggable')
var t = document.getElementById('target')

d.addEventListener('mousedown', doThis)
d.addEventListener('mouseup', thenThis)
d.addEventListener('mouseleave', thenThis)
d.addEventListener('mouseleave', alertLeave)

function doThis(){
  d.setAttribute('draggable', true)
  d.addEventListener('dragstart', dragStart)
  t.style.background = 'black'
  t.addEventListener('dragover', dragOver)
  t.addEventListener('drop', drop)
}

function thenThis(){
  d.setAttribute('draggable', false)
  d.removeEventListener('dragstart', dragStart)
  t.style.background = 'none'
  t.removeEventListener('dragover', dragOver)
  t.removeEventListener('drop', drop)
}

function dragStart(e){
  e.dataTransfer.setData('text/plain', e.target.id);
  e.dataTransfer.effectAllowed = 'move';
}

function dragOver(e){
  e.preventDefault();
}

function drop(e){
  var data = e.dataTransfer.getData('text/plain');
  e.preventDefault();
  e.target.parentElement.appendChild(document.getElementById(data));
}

function alertLeave(){
  //alert('mouseleave')
}
body {
  display: flex;
  justify-content: space-around;
}

div {
  width: 50px;
  height: 50px;
}

#draggable {
  background: purple;
}

#target {
  border: solid 3px black;
}
<div id="draggable"></div>
<div id="target"></div>

【问题讨论】:

  • 查看 MDN 的 HTML Drag and Drop API 里面有很多很好的例子和细节。您几乎可以复制并粘贴到您的项目中。
  • @RonRoyston 是的,我已经知道所有的事件,虽然我确信我仍然可以学到一些东西,但是一天中的时间太少而且太懒了大声笑

标签: javascript jquery html draggable


【解决方案1】:

问题是,当您尝试抓住靠近其右边缘的左侧元素时,mouseleave 在您将鼠标向右移动时立即被触发,而dragstart 有机会被触发。这段代码sn-p中可以看到事件:

var d = document.getElementById('draggable')
var t = document.getElementById('target')

d.addEventListener('mousedown', doThis)
d.addEventListener('mouseleave', thenThis)

function doThis(){
  console.log('mousedown');
  d.setAttribute('draggable', true);
  d.addEventListener('dragstart', dragStart);
  t.style.background = 'black';
  t.addEventListener('dragover', dragOver);
  t.addEventListener('drop', drop);
}

function thenThis(){
  console.log('mouseleave');
  d.setAttribute('draggable', false);
  d.removeEventListener('dragstart', dragStart);
  t.style.background = 'none';
  t.removeEventListener('dragover', dragOver);
  t.removeEventListener('drop', drop);
}

function dragStart(e){
  console.log('dragstart');
  e.dataTransfer.setData('text/plain', e.target.id);
  e.dataTransfer.effectAllowed = 'move';
}

function dragOver(e){
  e.preventDefault();
}

function drop(e){
  var data = e.dataTransfer.getData('text/plain');
  e.preventDefault();
  e.target.parentElement.appendChild(document.getElementById(data));
}
body {
  display: flex;
  justify-content: space-around;
}

.square {
  width: 50px;
  height: 50px;
}

#draggable {
  background: purple;
}

#target {
  border: solid 3px black;
}
<div id="draggable" class="square"></div>
<div id="target" class="square"></div>

为避免这种情况,您可以capture the mouse 而不是处理mouseleave。一旦拖动操作开始,您可以释放鼠标捕获并依赖dragend 事件。当用户释放鼠标(如果元素未被拖动)或拖动操作结束时,可拖动元素的属性将被重置。

var d = document.getElementById('draggable')
var t = document.getElementById('target')
var releaseCapture = null;

d.addEventListener('mousedown', mouseDown);
d.addEventListener('dragend', resetDraggableElement);

function mouseDown() {
  releaseCapture = captureMouse(resetDraggableElement);
  d.setAttribute('draggable', true);
  d.addEventListener('dragstart', dragStart);
  t.style.background = 'black';
  t.addEventListener('dragover', dragOver);
  t.addEventListener('drop', drop);
}

function resetDraggableElement() {
  d.setAttribute('draggable', false);
  d.removeEventListener('dragstart', dragStart);
  t.style.background = 'none';
  t.removeEventListener('dragover', dragOver);
  t.removeEventListener('drop', drop);
}

function dragStart(e) {
  if (releaseCapture) {
    releaseCapture();
    releaseCapture = null;
  }
  e.dataTransfer.setData('text/plain', e.target.id);
  e.dataTransfer.effectAllowed = 'move';
}

function dragOver(e) {
  e.preventDefault();
}

function drop(e) {
  var data = e.dataTransfer.getData('text/plain');
  e.preventDefault();
  e.target.parentElement.appendChild(document.getElementById(data));
}

function captureMouse(mouseUpHandler) {
  var releaseCapture = function() {
    document.removeEventListener("mouseup", lostCaptureHandler, false);
  }
  var lostCaptureHandler = function() {
    releaseCapture();
    if (mouseUpHandler) {
      mouseUpHandler();
    }
  };
  document.addEventListener("mouseup", lostCaptureHandler, false);
  return releaseCapture;
}
body {
  display: flex;
  justify-content: space-around;
}

.square {
  width: 50px;
  height: 50px;
}

#draggable {
  background: purple;
}

#target {
  border: solid 3px black;
}
<div id="draggable" class="square"></div>
<div id="target" class="square"></div>

【讨论】:

  • 您的解决方案的问题是它仍然可以通过dblclick 事件出现故障。尝试快速连续单击两次(或双击)紫色方块,而不是在第二次单击后松开鼠标按钮,继续按住它,然后在松开之前将光标滑出紫色方块。目标保持黑色,直到再次拖动元素。我只使用mousedownmouseup 也遇到了同样的问题,这就是为什么我也开始使用mouseleave
  • 我修改了第一个解决方案,只有在拖动开始时才使目标变黑。我不知道这是否对你有用。
  • 我相信这会奏效。
  • 我添加了指向原始帖子的链接(指向另一篇文章)用于鼠标捕获。
  • 通常,当鼠标事件发生(mousemove、mousedown等)时,鼠标下的元素会收到该事件。但是,当一个元素“捕获”了鼠标时,所有鼠标事件都会发送到该元素,即使鼠标不在该元素上也是如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-09
  • 1970-01-01
  • 2011-06-27
  • 2023-04-10
相关资源
最近更新 更多