点击触发Mouseleave

时间:2017-07-23 15:34:39

标签: javascript google-chrome dom mouseevent

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

DEMO: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

.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;
}

6 个答案:

答案 0 :(得分:11)

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

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

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

根据我在Chrome v64上的测试,只要快速点击导致null事件被触发,这两个值都将为mouseleaverelatedTarget用于较旧的浏览器兼容性

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

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

答案 1 :(得分:10)

这似乎是一个错误(我可以在Chrome中重现它,点击鼠标按下并且鼠标在彼此之后快速发生)。

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

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

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

答案 2 :(得分:4)

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

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

jsfiddle demo

这是复制品的GIF。单击线索区域(授予一些强度和移动),单击标签中的事件和输入框正在触发,然后您看到两个 mouseleave 事件触发错误,然后第三个鼠标真正触发离开了蓝色区域。

Sample reproduction

答案 3 :(得分:0)

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

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;
}

答案 4 :(得分:0)

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

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

答案 5 :(得分:-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
});