鼠标离开事件被点击触发

25

我有一个绝对定位的div,我想追踪鼠标何时移动到它上面以及何时离开它。不幸的是,有时在文本框中点击文本会触发mouseleave事件。

演示:js fiddle

我该如何防止这种情况发生?

JS:

let tooltip = document.createElement('div');
tooltip.innerHTML = 'HELLO WORLD';
tooltip.setAttribute('class', 'tooltip');
tooltip.style.display = 'none';

tooltip.onclick = evt => {
    console.log('click')
    evt.stopPropagation();
}
tooltip.ondblclick = evt => {
    console.log('double click')
    evt.stopPropagation();
}

tooltip.onmouseenter = () => {
    console.log('tooltip mouse OVER');
}

tooltip.onmouseleave = () => {
    console.log('tooltip mouse OUT')
}

tooltip.style.left = '290px';
tooltip.style.top = '50px';
tooltip.style.display = 'block';
document.body.appendChild(tooltip);

HTML

<div style="width: 300px; height: 300px; background-color: lightblue">

</div>

CSS

->

CSS(层叠样式表)

.tooltip {
    position: absolute;
    /*display: none;*/
    left: 100;
    top: 100;
    min-width: 80px;
    height: auto;
    background: none repeat scroll 0 0 #ffffff;
    border: 1px solid #6F257F;
    padding: 14px;
    text-align: center;
}

1
对我来说没问题:http://i.imgur.com/zFA9KXI.gifv - Jorge Fuentes González
你使用的是哪个浏览器?在Mozilla和Chrome上可以完美地运行。 - Vineesh
2
Chrome - 很少发生,当点击字符时。似乎与光标改变同时发生。 - Adam Rackis
我保证这不是什么玩笑 - 我知道重现可能很罕见。我使用的是 Chrome 最新稳定版(而非金丝雀版)。如果有人知道为什么可能会发生这种情况以及如何防止它,请告诉我。 - Adam Rackis
我遇到了一个类似的问题,你知道是否已经有人提交了bug吗? - cxw
显示剩余2条评论
6个回答

13

这似乎是一个错误(我可以在Chrome中重现它,使用鼠标按下和松开后快速发生的点击)。

我建议通过检查事件触发时鼠标是否仍停留在元素上来解决此问题:

tooltip.onmouseleave = (e) => {
    if (tooltip === document.elementFromPoint(e.clientX, e.clientY)) {
        console.log('false positive');
        return;
    }
    console.log('tooltip mouse OUT')
}

缺点是当浏览器窗口失去焦点时,这也被认为是误报。如果这对您来说是个问题,那么请查看这个答案


2
关于这个 bug,还有更多的信息吗?这似乎是一个非常严重的问题,但几乎没有人遇到过(我现在正在遇到)。触发它是否需要非常具体的条件? - Wouter Lievens
这是与Chromium相关的问题:https://bugs.chromium.org/p/chromium/issues/detail?id=798535 - rd3n
我曾认为elementFromPoint的支持不太好,因为MDN给出了一个“实验性技术”的警告 - 但从caniuse来看,在所有移动和桌面浏览器上似乎都得到了完全支持。 - Drenai

12

我之前查看过这里的答案和评论,但最近发现了一种方法来检查 mouseleave 事件是否被错误地触发。

我在我的 mouseleave 处理程序中添加了一个检查:

private handleMouseLeave(event: MouseEvent) {
    if(event.relatedTarget || event.toElement){
        // do whatever
    }
    // otherwise ignore
}

在Chrome v64上进行测试,当快速点击导致触发mouseleave事件时,这两个值都将为nullrelatedTarget是为了兼容旧的浏览器。

注意:如果鼠标离开target元素并进入浏览器(例如选项卡,菜单等),或者离开浏览器窗口,这两个值也将为null。对于我的用途来说,这不是一个问题,因为我正在处理一个滑动菜单,在我的特定情况下,离开浏览器窗口不应该关闭菜单。

注意:最新的Firefox版本(2018年2月)似乎会在每次单击我的菜单时触发mouseleave事件!必须深入研究一下。


1
发现得真好 - 肯定花了很长时间才找到 :) - T4NK3R
@T4NK3R 是的,整个星期六晚上都在比较不同事件的 MouseEvent 属性!!!感谢您的评论,在所有这些调试之后能得到一些反馈真是太好了 :-) - Drenai
不客气 - 这让我免去了在我的游戏中添加jQuery的麻烦 :) 另外:我在我的Firefox(61.0.2)中没有看到任何有趣的事情。 - T4NK3R

4

我也遇到了这个bug。在我的情况下,我将标签包装的复选框添加到列表中,并将列表包装在一个div中。我还使用了一些作为<hr>标签的列表项。如果你快速点击复选框和标签,你有时会触发包装div上的mouseleave事件。这不应该发生,因为所有被点击的元素都是div.wrapper的子元素。

...
  wrapper.addEventListener(
    'mouseleave',
    (e) => {
      logger('mouseleave fired');
      console.log('mouseleave fired', e);
    },
    false
  );
...

这里是 jsfiddle 演示

这是一个重现的 gif。在线索区域内单击(需要一定的强度和移动),标签和输入框的单击事件将触发,然后您会看到两个 mouseleave 事件错误地触发,当鼠标真正离开蓝色区域时,第三个事件才会触发。

样本重现


1
这不是对问题的答案。如果此问题的其他答案未解决您的问题,则可以提出新问题。一旦您拥有足够的声望,您还可以评论原始帖子,提供您对此明显错误的经验。 - trincot
谢谢您发布这个问题。虽然它只是一个回答,但它表明这是一个错误。我刚刚花了一个小时来尝试弄清楚为什么会发生这种情况!! - Drenai

2

@trincot的答案对我来说几乎奏效。在我的情况下,我正在处理弹出框。当我单击按钮时,它会触发弹出框出现在触发按钮上方。因此,document.elementFromPoint(e.clientX, e.clientY)返回的是弹出框元素而不是触发按钮。这是我解决问题的方法:

mouseleave(ev: MouseEvent) {
    const trigger: HTMLElement = document.getElementById('#myTrigger');
    const triggerRect = trigger.getBoundingClientRect();
    const falsePositive = isWithingARect(ev.clientX, ev.clientY, triggerRect);

    if (!falsePositive) {
        // do what needs to be done
    }
}

function isWithingARect(x: number, y: number, rect: ClientRect) {
  const xIsWithin = x > rect.left && x < rect.right;
  const yIsWithin = y > rect.top && y < rect.bottom;
  return xIsWithin && yIsWithin;
}

-1

使用MouseEvent.buttons检查主按钮是否被按下。

tooltip.onmouseleave = (e) => {
  if (e.buttons !== 1) {
    console.log('tooltip mouse OUT')
  }
}

-1
var trackmouseup = null;

$('.box').mouseup(function(event){
    if(trackmouseup){
        clearTimeout(trackmouseup);
    }
    trackmouseup = setTimeout(function(){
        trackmouseup = null;
    }, 2); //it must be 2ms or more

});


$('.box').mouseleave(function(event){
    //if this event is triggered by click, there must be a mouse up event triggered 2ms ago
    if(trackmouseup){
        return;
    }

    //to do something
});

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