Как захватить событие mouseup вне элемента mousedown?
Javascript onmouseup
событие не срабатывает, если кнопка мыши отпущена за пределы элемента, на котором onmousedown
был запущен.
Это вызывает ошибку перетаскивания в пользовательском интерфейсе JQuery: перетаскиваемый элемент JQuery не прекращает перетаскивание, когда кнопка мыши отпускается за пределы его контейнера (поскольку элемент перестанет двигаться при достижении своих родительских границ). Действия по воспроизведению:
- Перейдите на http://jqueryui.com/draggable/.
- Перетащите перетаскиваемый элемент вниз, пока мышь не покинет окружающий контейнер.
- Отпустите кнопку мыши (в данный момент кнопка мыши не нажата)
- Переместите мышь обратно в контейнер
- И перетаскиваемый все еще тащат. Я ожидаю, что перетаскивание прекратится, как только я отпущу кнопку мыши, независимо от того, где она отпущена.
Я вижу такое поведение в последних Chrome и IE.
Есть ли обходной путь?
Я знаю, что мы могли бы перестать тянуть контейнер на mouseout
или же mouseleave
, но я хотел бы продолжать перетаскивание, даже если я нахожусь за пределами родительского контейнера, так же, как в Google Maps (независимо от того, где вы отпускаете мышь, он всегда перестает перетаскивать карту).
4 ответа
Я нашел решение: просто прикрепите mouseup
обработчик события для document
вместо. Тогда он всегда будет отменен, даже если вы отпустите кнопку мыши за пределами браузера.
Конечно, это не очень хорошее решение, но, видимо, это единственный способ заставить перетаскивание работать правильно.
Вы можете сделать так, чтобы ваш элемент 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>
Лучший способ справиться с этим - проверить текущее состояние кнопки в обработчике события 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, потому что он будет перехвачен движением мыши, но я думаю, что он более читабелен, пока он там.
Если вы создадите файл «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>