[英]How to capture mouseup event outside of mousedown element? (i.e. for proper Drag-n-Drop?)
The Javascript onmouseup
event is not triggered if the mouse button is released outside the element on which onmousedown
has been triggered. onmouseup
如果在触发onmousedown
的元素外释放鼠标按钮,则不会触发 onmouseup 事件。
This causes a drag&drop bug in JQuery UI: A JQuery draggable element does not stop dragging when mouse button is released outside of its container (because the element will stop moving when reaching it's parent boundaries).这会导致 JQuery UI 中的拖放错误:当鼠标按钮在其容器外释放时,JQuery可拖动元素不会停止拖动(因为元素在到达其父边界时将停止移动)。 Steps to reproduce:
重现步骤:
I see that behavior in latest Chrome and IE.我在最新的 Chrome 和 IE 中看到了这种行为。
Is there any work-around?有什么解决方法吗?
I know that we could stop dragging the container on mouseout
or mouseleave
, but I would like to keep dragging, even if I am outside the parent container, much like in google maps (no matter, where you release the mouse, it always stops dragging the map).我知道我们可以在
mouseout
或mouseleave
上停止拖动容器,但我想继续拖动,即使我在父容器之外,就像在谷歌地图中一样(无论你在哪里释放鼠标,它总是停止拖动地图)。
You can have your mousedown element "capture" the pointer.您可以让 mousedown 元素“捕获”指针。 Then it would always receive the mouseup event.
然后它总是会收到 mouseup 事件。 In React this could look like this:
在 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}
/>
And here is an implementation using "plain vanilla" html + javascript:这是一个使用“普通香草”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>
I found the solution: Simply attach the mouseup
event handler to document
instead. 我找到了解决方案:只需将
mouseup
事件处理程序附加到document
。 Then it will always cancel, even if you release the mouse button outside the browser. 然后它将始终取消,即使您在浏览器外面释放鼠标按钮。
Of course, this is not a pretty solution, but apparently, this is the only way to get dragging to work correctly. 当然,这不是一个漂亮的解决方案,但显然,这是让拖动正常工作的唯一方法。
The best way to handle this is to check the current button status in the mousemove event handler. 处理此问题的最佳方法是检查mousemove事件处理程序中的当前按钮状态。 If the button is no longer down, stop dragging.
如果按钮不再按下,请停止拖动。
If you put the mouseup event on the document then you just move the problem up, the original problem will still show itself if you release the button outside of the document (or even outside of the browser window). 如果您将鼠标事件放在文档上,那么您只需将问题向上移动,如果您在文档外部(或甚至在浏览器窗口之外)释放按钮,原始问题仍会显示出来。
For example with jQuery 例如使用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
}
});
You could skip the mouseup event handler because it will be caught by the mousemove but I think it's more readable with it still there. 你可以跳过mouseup事件处理程序,因为它会被mousemove捕获,但我认为它仍然存在,它更具可读性。
If you create an "index.html" file with the following code in it (you can just copy and paste it entirely) it will show the working solution using just plain vanilla html + javascript.如果您创建一个包含以下代码的“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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.