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

Posted

技术标签:

【中文标题】如何在 mousedown 元素之外捕获 mouseup 事件? (即正确的拖放?)【英文标题】:How to capture mouseup event outside of mousedown element? (i.e. for proper Drag-n-Drop?) 【发布时间】:2014-01-31 04:05:09 【问题描述】:

如果在触发onmousedown的元素之外释放鼠标按钮,则不会触发javascript onmouseup event。

这会导致 JQuery UI 中的拖放错误:当鼠标按钮在其容器外释放时,JQuery draggable 元素不会停止拖动(因为元素在到达其父边界时将停止移动)。重现步骤:

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

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

有什么解决办法吗?

我知道我们可以在 mouseoutmouseleave 上停止拖动容器,但我想继续拖动,即使我在父容器之外,就像在 google maps 中一样(不管你在哪里松开鼠标,它总是停止拖动地图)。

【问题讨论】:

containment:'parent' ??? jsfiddle.net/4wX58 PS:我看到当鼠标按钮在文档外释放时,元素仍然可以拖动:( 【参考方案1】:

我找到了解决方案:只需将 mouseup 事件处理程序附加到 document 即可。然后它总是会取消,即使您在浏览器之外释放鼠标按钮。当然,这不是一个很好的解决方案,但显然,这是让拖动正常工作的唯一方法。

尝试以下解决方案:

您将看到“拖动结束”始终会发生,无论您在何处释放光标。 另外,为了防止在拖动时选择文本,我添加了一个unselectable class。

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>

【讨论】:

谢谢,我只是在寻找如何跟踪文档外的光标(或者如果有办法)并看到了这个。我没有注意到谷歌地图做到了,尽管我注意到其他一些网站没有,而且当他们没有时很烦人。无论如何,感谢您发布您自己的答案,它帮助了我! 这只是将问题向上移动,如果您在文档(或窗口)之外释放鼠标按钮,您会遇到同样的问题。这可以通过我的答案中的解决方案来克服。 @Mog0 不确定您提出索赔的依据是什么?你试过了吗? 我试过了。但是,请注意日期——我的回答是将近 7.5 年前,而现代浏览器现在已经大不相同了。请注意,我下面的解决方案存在 IE8 和 Firefox 之间的兼容性问题!【参考方案2】:

处理此问题的最佳方法是在 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 捕获,但我认为它仍然存在时更具可读性。

【讨论】:

我更喜欢这个解决方案,除了我发现属性(至少在 Firefox 上)是 mouse.buttons,而不是 mouse.button。 mouse.button 对我来说总是 0 @ErinDrummond 发现得很好。 evt.button == 0 表示 IE 当鼠标在元素上时,这个解决方案仍然只将dragging 设置为false,而不是在元素之外。此外,与您的说法相反,document 事件处理程序不同于所有其他事件处理程序,因为它仅限于document 甚至浏览器窗口,并且将始终 立即触发,无论您在哪里停止拖动。 @Domi - 这是一个非常古老的讨论,经过 6 年的浏览器开发,在我看来是非常不公平的。答案是基于当时使用 IE7/8 和同等版本的情况,所以如果现代浏览器的行为不同,因为当时的规范发生了显着变化,我一点也不感到惊讶。 这部分没有改变。我投了反对票,因为它从未解决问题中所述的问题。

以上是关于如何在 mousedown 元素之外捕获 mouseup 事件? (即正确的拖放?)的主要内容,如果未能解决你的问题,请参考以下文章

几个mouse事件的特点

js按下鼠标mousedown并mousemove的时候,如何保持鼠标样式全屏不变?

jquery 事件

捕获 NSEvent 以添加修饰符问题

mousedown/mouseup/click执行顺序

鼠标事件