Mouseleave, вызванный щелчком
У меня есть абсолютно позиционированный div, и я пытаюсь отслеживать, когда мышь перемещается по нему, и когда мышь уходит. К сожалению, нажатие на текст в поле иногда вызывает событие mouseleave.
DEMO: js скрипка
Как я могу предотвратить это?
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;
}
Ответы
Ответ 1
Кажется, это ошибка (я могу воспроизвести ее в Chrome с кликами, которые имеют мышь вниз и мышь быстро происходит друг за другом).
Я бы предложил обойти эту проблему, проверив, находится ли мышь по всему элементу в момент запуска события:
tooltip.onmouseleave = (e) => {
if (tooltip === document.elementFromPoint(e.clientX, e.clientY)) {
console.log('false positive');
return;
}
console.log('tooltip mouse OUT')
}
Недостатком является то, что когда окно браузера теряет фокус, это также считается ложным. Если это проблема для вас, то выберите этот ответ.
Ответ 2
Ранее я просматривал ответы и комментарии, но недавно нашел способ проверить, было ли ошибочно запущено событие mouseleave
Я добавил чек в мой обработчик mouseleave
:
private handleMouseLeave(event: MouseEvent) {
if(event.relatedTarget || event.toElement){
// do whatever
}
// otherwise ignore
}
Из моего тестирования в Chrome v64 оба этих значения будут null
всякий раз, когда быстрый щелчок вызывает событие mouseleave
. relatedTarget
предназначен для более старой совместимости с браузером
Примечание. Оба эти значения также будут null
, если мышь покинет элемент target
и войдет в браузер (например, вкладки, меню и т.д.) или покинет окно браузера. Для моих целей это не было проблемой, так как это скользящее меню, с которым я работаю, и оставить окно браузера не должно закрывать меню в моем конкретном случае.
Примечание: последняя версия Firefox (февраль 2018), кажется, запускает mouseleave
при каждом нажатии моего меню! Придется заглянуть в него
Ответ 3
Я также столкнулся с этой ошибкой. В моем случае я добавил флажки, завернутые в список, и завернул список в div. Я также использовал некоторые элементы списка, которые были тегами <hr>
. Если вы быстро нажмете флажки и метки, вы иногда запускаете событие mouseleave
на обертке div. Это не должно происходить, так как все щелкнутые элементы являются дочерними элементами div.wrapper
.
...
wrapper.addEventListener(
'mouseleave',
(e) => {
logger('mouseleave fired');
console.log('mouseleave fired', e);
},
false
);
...
jsfiddle demo
Здесь gif воспроизведения. Нажмите внутри области подсказки (предоставленной с некоторой интенсивностью и движением), щелкните события из метки и окна ввода, и вы увидите, что два события mouseleave срабатывают по ошибке, а затем третий, когда мышь действительно покидает синюю область.
![Пример воспроизведения]()
Ответ 4
Ответ @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;
}
Ответ 5
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
});