如果在已触发 onmouseup
的元素之外释放鼠标按钮,则不会触发Javascript
onmousedown
事件。
这会导致 JQuery UI 中出现拖放错误:当鼠标按钮在其容器外部释放时,JQuery draggable 元素不会停止拖动(因为该元素在到达其父边界时将停止移动)。重现步骤:
我在最新的 Chrome 和 IE 中看到了这种行为。
有什么解决办法吗?
我知道我们可以停止在
mouseout
或 mouseleave
上拖动容器,但我想继续拖动,即使我在父容器之外,就像在 google 地图 中一样(无论您在哪里释放鼠标,它总是停止拖动地图)。
您可以让 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}
/>
这是使用“plain vanilla”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 START\n';
});
document.addEventListener('mouseup touchend', (evt) => {
if (dragging) {
event.preventDefault();
dragEl.classList.remove('unselectable');
dragging = false;
logEl.textContent += 'drag END\n';
}
});
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>
setPointerCapture
API提供了更清晰的解决方案,如此答案中所述。
处理此问题的最佳方法是在 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>