简体   繁体   English

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

[英]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:重现步骤:

  • Go to http://jqueryui.com/draggable/ . Go 到http://jqueryui.com/draggable/
  • Drag the draggable downward until the mouse has left the surrounding container向下拖动 draggable 直到鼠标离开周围的容器
  • Release mouse button (no mouse button is pressed at this point)释放鼠标按钮(此时没有按下鼠标按钮)
  • Move mouse back into container将鼠标移回容器
  • And the draggable is still being dragged.而 draggable 仍在被拖动。 I would expect the dragging to have stopped as soon as I released the mouse button - no matter where it is released.我希望只要松开鼠标按钮,拖动就会停止 -无论它在何处松开。

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

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.

相关问题 捕获元素jquery之外的mousedown事件 - capture mousedown event outside of an element jquery 在mousedown元素之外捕获mouseup的最佳方法 - Best way to catch mouseup outside of mousedown element jQuery在元素内部检测mousedown,然后在元素外部进行mouseup - jQuery detect mousedown inside an element and then mouseup outside the element 如何在 Vuetify v-dialog 外部单击时关闭它,但在 mousedown 事件而不是 mouseup 上执行? - How to dismiss Vuetify v-dialog when clicked outside of it but do it on mousedown event rather than on mouseup? Javascript mouseup和mousedown事件 - Javascript mouseup and mousedown event 在浏览器/窗口之外捕获 mouseup 事件 - Capture mouseup event outside of browser/window 即使元素在 mousedown 和 mouseup 之间从光标下方移动,如何处理“单击”事件? - How to handle "click" event even if element is moved from beneath the cursor between mousedown and mouseup? HTML5:在没有mouseDown事件的情况下在元素上接收mouseUp事件 - HTML5: receiving mouseUp events on element without mouseDown event 当 mousedown/mouseup 在元素上添加/删除类时不发出单击事件 - Click event not emitted when mousedown/mouseup do add/removeClass on element Javascript mousedown和mouseup事件冲突 - Javascript mousedown and mouseup event conflict
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM