如何在mousedown元素之外捕获mouseup事件?(即用于正确的拖放?)



如果在触发onmousedown的元素外部释放鼠标按钮,则不会触发Javascriptonmouseup事件。

这导致拖动&JQuery UI中的drop bug:当鼠标按钮从容器外部释放时,JQuery可拖动元素不会停止拖动(因为元素在到达其父边界时会停止移动)。复制步骤:

  • 转到http://jqueryui.com/draggable/.
  • 向下拖动可拖动对象,直到鼠标离开周围的容器
  • 释放鼠标按钮(此时未按下鼠标按钮)
  • 将鼠标移回容器
  • 可拖动的仍在拖动。我希望只要我释放鼠标按钮,拖动就会停止-,无论它在哪里释放

我在最新的Chrome和IE中看到了这种行为。

周围有工作吗?

我知道我们可以停止在mouseoutmouseleave上拖动容器,但我想继续拖动,即使我在父容器之外,就像在谷歌地图中一样(无论你在哪里释放鼠标,它总是停止拖动地图)。

您可以拥有您的mousedown元素"捕获";指针。然后它将始终接收mouseup事件。在React中,这可能看起来像这样:

const onPointerDownDiv1 = (event: React.PointerEvent) => {
(event.target as HTMLDivElement).setPointerCapture(event.pointerId);
// Do something ...
};
const onPointerUpDiv1 = (event: React.PointerEvent) => {
(event.target as HTMLDivElement).releasePointerCapture(event.pointerId);
// Do something ...
};
<div
ref={div1}
id="div1"
className="absolute top-[200px] left-[390px] h-8 w-8 bg-red-300"
onPointerDown={onPointerDownDiv1}
onPointerUp={onPointerUpDiv1}
/>

这里是使用";普通香草";html+javascript:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div
id="div1"
style="
position: absolute;
left: 50px;
top: 50px;
width: 20px;
height: 20px;
background-color: red;
"
></div>
</body>
<script>
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
let divElement = document.getElementById("div1");
divElement.addEventListener("pointerdown", onPointerDown);
divElement.addEventListener("pointermove", onPointerMove);
divElement.addEventListener("pointerup", onPointerUp);
function onPointerDown(event) {
divElement.setPointerCapture(event.pointerId);
offsetX = event.clientX - divElement.offsetLeft;
offsetY = event.clientY - divElement.offsetTop;
isDragging = true;
}
function onPointerMove(event) {
if (isDragging) {
divElement.style.left = (event.clientX - offsetX).toString() + "px";
divElement.style.top = (event.clientY - offsetY).toString() + "px";
}
}
function onPointerUp(event) {
divElement.releasePointerCapture(event.pointerId);
isDragging = false;
}
</script>
</html>

我发现这是最好的解决方案:将mouseup事件处理程序附加到document。然后它将始终取消,即使您在浏览器外释放鼠标按钮。当然,这不是一个很好的解决方案,但显然,这是使拖动正确工作的唯一方法。

尝试以下解决方案:

  • 你会看到"拖动结束";无论您在哪里释放光标,都将始终发生
  • 此外,为了防止在拖动时选择文本,我添加了一个unselectable

let dragging = false;
const dragEl = document.querySelector('div');
const logEl = document.querySelector('pre');
dragEl.addEventListener('mousedown touchstart', (evt) => {
dragging = true;
dragEl.classList.add('unselectable');
logEl.textContent += 'drag STARTn';
});
document.addEventListener('mouseup touchend', (evt) => {
if (dragging) {
event.preventDefault();
dragEl.classList.remove('unselectable');
dragging = false;
logEl.textContent += 'drag ENDn';
}
});
div {
background: red;
}
.unselectable {
-webkit-user-select: none; /* Safari */        
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}
<div>drag me</div>
<hr>
LOG:
<p><pre></pre></p>

更新

这些天,setPointerCaptureAPI提供了一个更清洁的解决方案,如本回答中所解释的。

处理此问题的最佳方法是在mousemove事件处理程序中检查当前按钮状态。如果按钮不再向下,请停止拖动。

如果将mouseup事件放在文档上,然后将问题上移,那么如果在文档外部(甚至在浏览器窗口外部)释放按钮,原始问题仍然会显示出来。

例如使用jQuery

$div.on("mousedown", function (evt) {
dragging = true;
}).on("mouseup", function (evt) {
dragging = false;
}).on("mousemove", function (evt) {
if (dragging && evt.button == 0) {
//Catch case where button released outside of div
dragging = false;
}
if (dragging) {
//handle dragging here
}
});

你可以跳过mouseup事件处理程序,因为它会被mousemove捕获,但我认为它仍然在那里更可读。

如果您创建一个;index.html";文件中包含以下代码(您可以完全复制并粘贴它),它将显示使用普通html+javascript的工作解决方案。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div
id="div1"
style="
position: absolute;
left: 50px;
top: 50px;
width: 20px;
height: 20px;
background-color: red;
"
></div>
</body>
<script>
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
let divElement = document.getElementById("div1");
divElement.addEventListener("pointerdown", onPointerDown);
divElement.addEventListener("pointermove", onPointerMove);
divElement.addEventListener("pointerup", onPointerUp);
function onPointerDown(event) {
divElement.setPointerCapture(event.pointerId);
offsetX = event.clientX - divElement.offsetLeft;
offsetY = event.clientY - divElement.offsetTop;
isDragging = true;
}
function onPointerMove(event) {
if (isDragging) {
let newPosLeft = event.clientX - offsetX;
if (newPosLeft < 30) {
newPosLeft = 30;
}
let newPosTop = event.clientY - offsetY;
if (newPosTop < 30) {
newPosTop = 30;
}
divElement.style.left = newPosLeft.toString() + "px";
divElement.style.top = newPosTop.toString() + "px";
}
}
function onPointerUp(event) {
divElement.releasePointerCapture(event.pointerId);
isDragging = false;
}
</script>
</html>

最新更新