Mouseleave triggered by click
Asked Answered
H

6

26

I have an absolutely-positioned div, and I'm trying to keep track of when the mouse moves over it, and when the mouse leaves. Unfortunately clicking on the text in the box occasionally triggers the mouseleave event.

DEMO: js fiddle

How can I prevent this?

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;
}
Highstepper answered 23/7, 2017 at 15:34 Comment(7)
Is fine for me: i.imgur.com/zFA9KXI.gifvDosh
Which browser you are trying? this is perfectly working in mozilla and chrome.Mcclinton
Check extensions and such tooDosh
Chrome - it happens rarely, when clicking around the characters. Seems to coincide with the cursor changing.Highstepper
I promise this isn't some kind of joke - I know the repro can be rare. I'm on Chrome latest stable (not canary). If anyone knows why this might happen and how to prevent it, I'd love to hear.Highstepper
I am having a problem that I think is similar. Is there a bug open for it?Ambros
Chrome has this issue.Bionomics
V
14

This seems to be a bug (I could reproduce it in Chrome with clicks that have the mouse down and mouse up happening rapidly after each other).

I would suggest to work around this issue by checking whether the mouse is still over the element at the moment the event is fired:

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

The downside is that when the browser window loses focus, that is also considered a false positive. If that is an issue for you, then check this answer.

Valarievalda answered 23/7, 2017 at 16:6 Comment(3)
Is there any more information on this bug, it seems like this is a very major issue yet almost no one is experiencing it (I am right now)? Are there very specific conditions required to trigger it?Sniper
Here is the Chromium related issue: bugs.chromium.org/p/chromium/issues/detail?id=798535Wald
I thought elementFromPoint wasn't well supported, as MDN gives an experimental technology warning - but from caniuse, it's appears to be fully supported across all mobile and desktop browsersCalotte
C
12

I had previously looked at the answers and comments here, but recently found a way to check if the mouseleave event was triggered erroneously

I added a check in my mouseleave handler:

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

From my testing on Chrome v64, both of these values will be null whenever fast clicking causes the mouseleave event to be triggered. The relatedTarget is for older browser compatibility

Note: both of these values will also be null if the mouse leaves the target element and enteres the Browser (e.g. the tabs, menus etc), or leaves the browser window. For my purposes that was not a problem, as it is a sliding menu I am working with, and leaving the Browser window should not close the menu in my particular case.

Note: latest Firefox release (Feb 2018) seems to trigger mouseleave on every click of my menu! Will have to look into it

Calotte answered 22/2, 2018 at 13:19 Comment(3)
Well spotted - must have taken ages to find : )Thanasi
@Thanasi Yes, it took an entire Saturday evening comparing the MouseEvent properties of the different events!!! Thanks for your comment, nice to have some feedback after all that debugging:-)Calotte
You're welcome - saved me from having to add jQuery to my game : ) - BTW: Not seeing any funny business in my Firefox (61.0.2)..Thanasi
D
4

I also ran into this bug. In my case, I added label wrapped checkboxes into a list, and wrapped the list in a div. I also used some list items that were <hr> tags. If you click around the checkboxes and labels quickly you will occasionally trigger a mouseleave event on the wrapping div. This shouldn't occur as all clicked elements are children of the div.wrapper.

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

jsfiddle demo

Here's a gif of the reproduction. Click within the clue area (granted with some intensity and movement), click events from the label and input boxes are firing and then you see two mouseleave events fire in error, and then a third when the mouse truly leaves the blue area.

Sample reproduction

Delmardelmer answered 16/8, 2017 at 0:7 Comment(2)
This is not an answer to the question. If the other answer to this question does not solve your issue, then you could ask a new question. Once you have enough reputation, you can also comment to the original post, providing your experience with this apparent bug.Valarievalda
Thanks for posting this. It's an answer is so much as it shows that it's a bug. I've just spent an hour tryign to figure out why this was occurring!!Calotte
S
2

The answer by @trincot almost worked for me. In my case I'm dealing with popovers. When I click on a button, it triggers a popover showing up on top of the triggering button. So document.elementFromPoint(e.clientX, e.clientY) returns the popover element rather than the triggering button. Here's how I solved this:

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;
}
Surroundings answered 17/1, 2019 at 22:23 Comment(0)
L
-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
});
Laurelaureano answered 18/4, 2018 at 6:27 Comment(0)
P
-1

Check if the primary button is pressed with MouseEvent.buttons.

tooltip.onmouseleave = (e) => {
  if (e.buttons !== 1) {
    console.log('tooltip mouse OUT')
  }
}
Prunelle answered 9/9, 2020 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.