如果 "mousemove" 和 "click" 事件同时触发,应该怎么办?

25

我不知道这是否只是Chrome的问题(现在无法检查),但让我们尝试以下代码片段,其中绑定了两个事件到某些元素:

我不确定这是否只是Chrome的问题(现在无法检查),但让我们尝试一下下面的代码段,在其中将两个事件绑定到某个元素上:

$("div").on({
    mousemove: function(e) {
        console.log("move");
    },
    click: function(e) {
        console.log("click");
    }
});

如果我们尝试点击该元素,我们会发现由于某些原因mousemove事件立即在点击后触发,因此在控制台中我们会看到:

>> ...
>> click
>> move

演示: http://jsfiddle.net/gKqVt/

请注意,mousedownmouseup事件的工作方式相同。

我在SO上看到了许多关于同一问题的问题,但在我的搜索中没有一个(给出)明确的想法,如何才能仅触发click事件。


2
老鼠一直在移动,问题在于您的程序依赖于点击时鼠标不移动。 - stark
1
在Chrome 25(OSX)上,“click”在单击时触发,“move”在移动时触发。没有任何问题。 - Oleg
1
在OSX上,FF 17也可以像@Oleg所描述的那样工作。 - bfavaretto
3
我在Chrome 24上看到了这个,但在Firefox 18、IE 9或Opera 11上没有。 - Guffa
2
对我来说,在Chrome 24 Windows 7上,即使我没有动鼠标(甚至没有触碰它),mousemove事件也会被触发数百次。似乎是某个触发器导致了这个奇怪的bug。 - A. Wolff
显示剩余11条评论
7个回答

9

在Chrome浏览器中,mousemove似乎绑定到了每一个鼠标动作上,因此需要在每一次鼠标“移动”时存储鼠标位置,并将其与上一次鼠标位置进行比较以验证它确实已经“移动”。

var currentPos=[];
$("div").on({
    mousemove: function(e) {
        if (e.pageX!==currentPos[0] && e.pageY !==currentPos[1]){
            currentPos=[e.pageX,e.pageY];
        this.innerHTML = "Event: " + e.type;
        console.log("move");
        }
    },
    click: function(e) {
        this.innerHTML = "Event: " + e.type;
        console.log("click");
    }
});

Demo | Source


我猜你不应该混淆 window.evente 对象。否则,你的解决方案也是有道理的。 - VisioN
时间戳的黑客技巧对我没用,但这个可以。 - Justin
这是一个不错的解决方案,但如果点击事件添加了鼠标移动事件监听器来启动拖动监听序列,则无法正常工作。 - dudewad

7
这似乎是Chrome中的一个错误,最早于11月份报告,并且仍然存在。 Chromium问题161464 如果您特别针对Chrome,则可能值得比较事件时间戳以解决此问题(使用某些最小增量时间,如@ExplosionPills建议)。但是,如果您正在寻找一般行为,那么最好将它们视为单独的事件,因为除了Chrome(和可能的Safari?该错误标记为webkit-core),它们实际上将是单独的事件。

3
这种行为很奇怪,似乎并不普遍发生(在Chrome / IE中发生,但FFX没有)。我认为你没有得到一个直接的答案,因为实际上并没有一个。
可能是鼠标被点击动作轻微移动,但这可能不是原因。可能只是浏览器的怪癖。这些甚至似乎不是同一个事件,因为click中的stopImmediatePropagation不会阻止mousemove的触发。如果您聚焦于元素并按下键盘按钮,它将实际触发click和仅click。
由于这很古怪,似乎唯一的处理方法是时间。尽管这很笨拙,但我确实注意到click发生在mousemove之前1毫秒,所以您可以通过比较click时间戳+2(或10)来接近。
mousemove: function(e) {
    if ($(this).data('lastClick') + 10 < e.timeStamp) {

http://jsfiddle.net/gKqVt/3/

这很具体,你应该考虑不要在mousemove事件上立即发生行为,因为它非常频繁。


+1 这是一个好建议。为了让 mousemove 事件从一开始就起作用,您需要添加对未定义数据的检查,例如:http://jsfiddle.net/gKqVt/4/. - VisioN
@VisioN 我对此没有问题,但我同意你的更加技术上正确。 - Explosion Pills
问题在于 undefined + 10 === NaN,而 NaN < e.timeStamp 的结果是 false,因此如果没有设置默认值,movemove 事件将不会被触发。 - VisioN
在 Firefox 中,我发现即使只是单击,mousemove 事件也会触发。 - SuperUberDuper
我可以确认在Windows 10上的Chrome 69中,mousemove事件会在按钮的点击处理程序触发之前在body和document上触发。请参见https://codepen.io/MSCAU/pen/mzOBpX。 - MSC
显示剩余3条评论

3

为什么不像下面这样检查鼠标是否真正移动:

function onMouseDown (e) {
    mouseDown = { x: e.clientX, y: e.clientY };
    console.log("click");
}

function onMouseMove (e) {
    //To check that did mouse really move or not
    if ( e.clientX !== mouseDown.x || e.clientY !== mouseDown.y) {
        console.log("move");
    }
}

演示链接

(我认为它在所有浏览器中仍然是正确的)


0
我注意到这种行为是在我需要区分mousedown和mouseup而不需要在两者之间拖动以及mousedown和mouseup之间拖动时发生的,我使用的解决方案如下:
var div = $('#clickablediv');
var mouseDown = false;
var isDragging = 0;
div.mousedown(function () {
   isDragging = false;
       mouseDown = true;
   }).mousemove(function () {
       if (mouseDown) isDragging++;
   }).mouseup(function () {
       mouseDown = false;
       var wasDragging = isDragging;
       isDragging = 0;
       if (!wasDragging || wasDragging<=1) {
           console.log('there was no dragging');
       }
   });

当我尝试它时,我注意到定期点击会使"isDragging"等于3,但并不是非常频繁。

0
我在我的mouseMove(event)函数中添加了以下内容:
      function mouseMove(event)
      {
        if ((event.movementX == 0) && (event.movementY == 0)) return;

不清楚为什么没有移动时它会触发,但这对我有用。我至少在Chrome 102.0.5005.61上遇到了这个问题。几年前它并没有发生。


0

var a,b,c,d;
  $(".prd img").on({
 mousedown: function(e){
   a= e.clientX, b= e.clientY;
 },
 mouseup: function(e){
   c= e.clientX, d= e.clientY;
   if(a==c&&b==d){
     console.log('clicked');
   }
 }
  });

尝试这个。这一个工作正确。

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