关于javascript:点击触发触发

Mouseleave triggered by click

我有一个绝对定位的div,我试图跟踪鼠标何时在其上移动以及鼠标何时离开。 不幸的是,偶尔单击框中的文本会触发mouseleave事件。

演示:js小提琴

我该如何预防?

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

1
 

的CSS

1
2
3
4
5
6
7
8
9
10
11
12
.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;
}


我以前曾在这里查看过答案和评论,但是最近找到了一种检查mouseleave事件是否被错误触发的方法

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

1
2
3
4
5
6
private handleMouseLeave(event: MouseEvent) {
    if(event.relatedTarget || event.toElement){
        // do whatever
    }
    // otherwise ignore
}

根据我在Chrome v64上的测试,每当快速单击导致触发mouseleave事件时,这两个值都将为nullrelatedTarget用于与旧版浏览器兼容

注意:如果鼠标离开target元素并进入浏览器(例如选项卡,菜单等)或离开浏览器窗口,则这两个值也将均为null。对我而言,这不是问题,因为它是我正在使用的滑动菜单,并且在我的情况下,离开浏览器窗口不应关闭菜单。

注意:最新的Firefox版本(2018年2月)似乎在我的菜单上单击每次都会触发mouseleave!将不得不研究它


这似乎是一个错误(我可以在Chrome中通过单击使鼠标按下和鼠标向上快速发生的点击来复制它)。

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

1
2
3
4
5
6
7
tooltip.onmouseleave = (e) => {
    if (tooltip === document.elementFromPoint(e.clientX, e.clientY)) {
        console.log('false positive');
        return;
    }
    console.log('tooltip mouse OUT')
}

缺点是,当浏览器窗口失去焦点时,这也被认为是误报。如果这是您的问题,请检查此答案。


我也遇到了这个错误。就我而言,我将标签包装的复选框添加到列表中,并将列表包装在div中。我还使用了一些标记的列表项。如果快速单击复选框和标签,则偶尔会在包装div上触发mouseleave事件。由于所有单击的元素都是div.wrapper的子元素,因此不应发生这种情况。

1
2
3
4
5
6
7
8
9
10
...
  wrapper.addEventListener(
    'mouseleave',
    (e) => {
      logger('mouseleave fired');
      console.log('mouseleave fired', e);
    },
    false
  );
...

jsfiddle演示

这是复制品的gif图像。在提示区域内单击(允许一定的强度和移动),单击标签中的事件,并触发输入框,然后您看到两个错误触发的mouseleave事件,然后在鼠标真正离开蓝色区域时看到第三个事件。

Sample reproduction


@trincot的回答几乎对我有用。就我而言,我正在处理弹出窗口。当我单击一个按钮时,它会触发一个弹出窗口,该弹出窗口显示在触发按钮的顶部。因此document.elementFromPoint(e.clientX, e.clientY)返回popover元素而不是触发按钮。这是我解决此问题的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
});