JavaScript:如何检查鼠标是否按下?

177

有没有一种方法可以在JavaScript中检测鼠标按钮是否正在按下?

我知道"mousedown"事件,但那不是我需要的。我想在鼠标按钮按下后的一段时间内,能够检测它是否仍然按下。

这个可能吗?


29
为什么你会接受一个并没有回答你问题的答案?注册mousedown和mouseup是错误的(因为你可以在浏览器外释放鼠标并让它认为你仍在拖动)。当你遇到问题并来到这个页面时,这完全没有用。 - Jack
3
@Jack,它回答了我的问题。请先阅读我的问题,然后再阅读答案。显然有超过100个人认为这是一个很好的答案。 - TM.
6
是的,但我、阿尔弗雷德和其他8个人告诉你,跟踪鼠标按下和松开并不是正确的方法。正确的答案应该涉及在您选择的处理程序中分析事件对象,或者分析所有与鼠标相关的事件(如进入/退出),如果您需要基于时间的检查而非事件驱动的检查。 - Jack
1
这个问题是7年前提出并得到回答的。我不会在有新答案时收到通知(但对于评论我会)。写一个更好的答案,社区会将其投票到顶部 :) 很多信息会过时,事情也会改变。 - TM.
3
抱歉打扰了,但是......那个信息永远不会到达顶部。一旦被接受的非自我回答将永远保持在顶部。此外,“很多人投票支持这个答案”并不意味着这个答案是好的;只是因为许多人看到它,认为它还不错,并且点击了赞同,因为它对他们有用。 - anon
显示剩余2条评论
17个回答

168
关于Pax的解决方案:如果用户有意或无意地点击多个按钮,则该方案将无效。不要问我怎么知道的 :-(。
正确的代码应该像这样:
var mouseDown = 0;
document.body.onmousedown = function() { 
  ++mouseDown;
}
document.body.onmouseup = function() {
  --mouseDown;
}

有这样的测试:

if(mouseDown){
  // crikey! isn't she a beauty?
}

如果您想知道哪个按钮被按下,需要准备将mouseDown设为计数器数组,并分别对不同的按钮进行计数:
// let's pretend that a mouse doesn't have more than 9 buttons
var mouseDown = [0, 0, 0, 0, 0, 0, 0, 0, 0],
    mouseDownCount = 0;
document.body.onmousedown = function(evt) { 
  ++mouseDown[evt.button];
  ++mouseDownCount;
}
document.body.onmouseup = function(evt) {
  --mouseDown[evt.button];
  --mouseDownCount;
}

现在你可以准确地检查哪些按钮被按下了:
if(mouseDownCount){
  // alright, let's lift the little bugger up!
  for(var i = 0; i < mouseDown.length; ++i){
    if(mouseDown[i]){
      // we found it right there!
    }
  }
}

现在请注意,上面的代码仅适用于符合标准的浏览器,这些浏览器从0开始传递按钮编号。IE使用当前按下的按钮的位掩码:
  • 0表示“没有按下任何键”
  • 1表示左键
  • 2表示右键
  • 4表示中键
  • 以上任意组合,例如左+中为5
因此,请相应地调整您的代码!我将其留作练习。
同时,请记住:IE使用名为“event”的全局事件对象。
顺便说一下,IE有一个对您有用的功能:当其他浏览器仅在鼠标按钮事件(onclick、onmousedown和onmouseup)中发送“button”时,IE也在onmousemove中发送它。因此,当您需要知道按钮状态时,可以开始监听onmousemove,并在获取到它后立即检查evt.button——现在您知道按下了哪些鼠标按钮了。
// for IE only!
document.body.onmousemove = function(){
  if(event.button){
    // aha! we caught a feisty little sheila!
  }
};

当她装死不动的时候,你什么也得不到。

相关链接:

更新#1:我不知道为什么我会带上document.body-style的代码。直接将事件处理程序附加到文档中会更好。


6
我觉得这在Chrome中不会起作用,因为如果原始的mousedown发生在滚动条上,Chrome不会生成mouseup事件。 这是一个例子,来自这个Chrome / Chromium问题。所以如果有人使用滚动条,你的左鼠标按钮将永远按下!(在Chrome中) - KajMagnus
3
我喜欢你的解决方案,但如果鼠标来自于不同的应用程序,比如说 MS Word,并且正在拖动一些文本,那该怎么办?你如何检测到鼠标按下的状态? - Shadrack Orina
6
为什么在第二个例子中你要逐个增加mousedown数组元素的值,我不理解这个操作的目的。为什么对于每个按钮都使用计数器而不是布尔值? - amwinter
6
这是一个非常流行的问题(和答案)。我很惊讶这个问题还没有出现,但你根本不需要存储鼠标数据。您可以将 event.buttons 直接插入您正在使用的任何事件触发器中,而无需外部保存变量。我提出了一个全新的问题(因为我的 mouseup 事件并不总是触发,所以这个解决方案对我不起作用),然后在最多5分钟内得到了那个答案。 - RoboticRenaissance
3
我认为此回答存在一个相当严重的问题。如果你在窗口内按下鼠标然后释放鼠标键,或者根本就右键单击,mouseup 事件将不会被触发,你的值将保持大于0,导致检查永久失败。 - thedayturns
显示剩余12条评论

79

这是一个旧问题,这里的答案似乎大多主张使用mousedownmouseup来跟踪按钮是否被按下。但正如其他人指出的,mouseup只会在浏览器内执行时触发,这可能会导致丢失按钮状态。

然而,MouseEvent(现在)指示当前按下哪些按钮:

  • 对于所有现代浏览器(包括Safari v11.1+ [v11.3+ on iOS]),请使用MouseEvent.buttons
  • 对于Safari < 11.1(iOS上的11.3),请使用MouseEvent.which(对于Safari来说,buttons将未定义)。注意:which对于右键和中键单击使用与buttons不同的数字。

当在document上注册时,mousemove将立即在光标重新进入浏览器时触发,因此如果用户在外部释放,则状态将在他们鼠标返回内部后立即更新。

一个简单的实现可能如下:

var primaryMouseButtonDown = false;

function setPrimaryButtonState(e) {
  var flags = e.buttons !== undefined ? e.buttons : e.which;
  primaryMouseButtonDown = (flags & 1) === 1;
}

document.addEventListener("mousedown", setPrimaryButtonState);
document.addEventListener("mousemove", setPrimaryButtonState);
document.addEventListener("mouseup", setPrimaryButtonState);

该代码跟踪主要鼠标按钮(通常是左键)的状态,忽略其他鼠标按钮的状态。

如果需要更复杂的情况(不同的按钮/多个按钮/控制键),请查看MouseEvent文档。


我有一个情况,希望在鼠标移动期间获取按钮代码为“1”,但它产生了“7” - 所有工作都在Chrome上。 有人有什么想法吗? - Vasily Hall
@VasilyHall 看MDN关于MouseEvent.buttons的文档,似乎数字7表示它认为主、次和辅助按钮都被同时按下了。 不确定为什么会这样 - 你使用了高级鼠标吗?如果是这样,也许值得尝试使用基本鼠标。 如果你只是需要让它工作,可以使用位运算检查,确保只有左键被按下并忽略其他任何键。例如, MouseEvent.buttons & 1 === 1 - Jono Job
谢谢,我在Adobe Illustrator应用程序内使用嵌入式Chrome(ium?)浏览器来运行我的JavaScript-我敢打赌所有各种事物的组合以某种方式导致了7,尽管只有一个按钮被按下。由于这在我的应用程序中是一致的,我只是检查1或7以促进我的交互性。我要补充说的是,虽然我使用Logitech M570(或其他)轨迹球鼠标,在常规浏览器中它可以正确地注册1,但在Illustrator中则为7。 - Vasily Hall
1
为什么从下往上阅读 Stack Overflow 页面更好的另一个例子。 - Dino Dini

22

我认为最好的方法是保留鼠标按钮状态的记录,具体实现如下:

var mouseDown = 0;
document.body.onmousedown = function() { 
    mouseDown = 1;
}
document.body.onmouseup = function() {
    mouseDown = 0;
}

然后,在您的代码中稍后:

if (mouseDown == 1) {
    // the mouse is down, do what you have to do.
}

+1,谢谢。我本以为可能不得不采取这种方法,但我希望有一些功能可以直接检查状态。 - TM.
23
不使用布尔值的原因是什么? - moala
2
@moala 我认为Int看起来打字更少,最终性能略微更好:http://jsperf.com/bool-vs-int - Johnathan Elmore
1
也许他实际上是一个MySQL数据库管理员 ;) - Stephen Paul

13

解决方案不好。如果在文档上按下鼠标,然后在浏览器外面“松开”鼠标,此时浏览器仍会认为鼠标是被按下的状态。

唯一好的解决方案是使用 IE 事件对象。


18
我明白你的想法,但仅适用于IE浏览器的解决方案也不是一个好的解决方案。 - TM.
@alfred 在某些情况下,了解mousemove是否超出窗口可能会有所帮助。如果(event.target.nodeName.toLowerCase ==='html')down = 0; - B.F.

7

我知道这是一篇旧帖子,但我觉得使用鼠标上下键跟踪鼠标按钮有些笨拙,所以我找到了另一种可能会吸引一些人的替代方法。

<style>
    div.myDiv:active {
        cursor: default;
    }
</style>

<script>
    function handleMove( div ) {
        var style = getComputedStyle( div );
        if (style.getPropertyValue('cursor') == 'default')
        {
            // You're down and moving here!
        }
    }
</script>

<div class='myDiv' onmousemove='handleMove(this);'>Click and drag me!</div>

:active选择器在处理鼠标点击时要比mousedown/up更好,您只需要在onmousemove事件中读取该状态即可。为了实现这一点,我需要作弊并依赖于默认光标为“auto”的事实,并将其更改为“default”,这是默认情况下自动选择的内容。

您可以使用由getComputedStyle返回的对象中的任何东西作为标志,而不会影响您页面的外观,例如border-color。

我本来希望在:active部分设置自己定义的用户样式,但我无法使其工作。如果可能的话,最好。


谢谢分享这个。我正在使用这个想法来实现拖放样式逻辑,但在IE上遇到了问题,不得不包含额外的逻辑 - Eyup Yusein

6

如果您正在处理一个具有现有鼠标事件处理程序的复杂页面,我建议在捕获阶段处理事件(而不是冒泡)。要做到这一点,只需将addEventListener的第3个参数设置为true

此外,您可能需要检查event.which以确保您正在处理实际的用户交互而不是鼠标事件,例如:elem.dispatchEvent(new Event('mousedown'))

var isMouseDown = false;

document.addEventListener('mousedown', function(event) { 
    if ( event.which ) isMouseDown = true;
}, true);

document.addEventListener('mouseup', function(event) { 
    if ( event.which ) isMouseDown = false;
}, true);

将处理程序添加到文档(或窗口)而不是文档主体很重要,因为它确保记录窗口外的鼠标松开事件。


5

如果其他人遇到这个问题,可以使用.matches:active选择器:

function mouseDown() {
    return document.body.matches(":active");
}

一个注意事项:这个方法在我的安卓平板上不起作用(我怀疑如果我现在有一个安卓手机来测试的话,也不会起作用)。我仍然使用这个技巧来确保拖动操作即使我错过了mouseup/touchend事件也能结束,但我需要检查当拖动操作开始document.body.matches(":active")是否为真,并且只有在这种情况下将其用作拖动结束的额外测试。 - kshetline
它在任何点击时都会解析整个DOM。 - NVRM
@kshetline 是的,显然它不适用于移动设备,也不适用于移动开发工具。 - illogicalapple

5

使用MouseEvent API来检测是否按下了任何鼠标按钮:

// Mouse buttons
document.addEventListener('mousedown', e => console.log(e.buttons))
// Keyboard keys
document.addEventListener('keydown', e => console.log(e.key))

返回值

A number representing one or more buttons. For more than one button pressed simultaneously, the values are combined (e.g., 3 is primary + secondary).

0 : No button or un-initialized
1 : Primary button (usually the left button)
2 : Secondary button (usually the right button)
4 : Auxilary button (usually the mouse wheel button or middle button)
8 : 4th button (typically the "Browser Back" button)
16 : 5th button (typically the "Browser Forward" button)

问题问的是如何_不_使用mousedown - undefined

4
以下代码片段将尝试在鼠标按下事件发生后2秒执行“doStuff”函数。如果用户松开按钮,则会触发mouseUp事件并取消延迟执行。
我建议使用一种跨浏览器事件附加方法 - 明确设置mousedown和mouseup属性是为了简化示例。
function doStuff() {
  // does something when mouse is down in body for longer than 2 seconds
}

var mousedownTimeout;

document.body.onmousedown = function() { 
  mousedownTimeout = window.setTimeout(doStuff, 2000);
}

document.body.onmouseup = function() {
  window.clearTimeout(mousedownTimeout);
}

谢谢,但这不是我寻找的行为(尽管我可以理解我的问题表明这正是我想做的)。 - TM.

2

您需要处理MouseDown和MouseUp事件,并设置一些标志或其他内容来跟踪它“稍后在路上”... :(


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接