Почему я не могу надежно захватить событие mouseout?

Мне нужно знать, когда курсор мыши оставляет div. Поэтому я подключаю событие mouseout. Однако, если я очень быстро перемещаю мышь из div, событие mouseout не срабатывает. Это правильно: курсор мыши сидел неподвижно внутри div, теперь он находится вне div, и все же обратный вызов mouseout не вызывался. (Он отлично работает, если я не двигаю мышью так быстро.)

Это правда в последней версии Google Chrome, поэтому не просто проблема с "старыми браузерами".

Обходной путь:

Вопрос об этой проблеме был перед. По-видимому, это просто факт жизни, и единственным обходным решением, которое я нашел, является просмотр событий mousemove вручную, каждый раз проверяя координаты курсора x/y и видя, попадают ли они в ограничивающий прямоугольник div поэтому у вас больше шансов "заметить", если курсор больше не находится внутри него.

По сравнению с тем, чтобы браузер делал все это изначально, выполнение вычислений при каждом перемещении пикселей немного влияет на производительность. Это также утомительно для кода.

На мой вопрос...

Почему браузер не может надежно захватить событие mouseout? Если я могу с уверенностью сказать, когда мышь оставила div, используя описанное выше решение, почему браузер не может это сделать?

Я понимаю (из приведенного выше ответа), что JavaScript не пытается интерполировать "фреймы". Скажем, если вы положили обработчик mousemove на document и быстро переместите указатель мыши на 200 пикселей вправо в идеальной горизонтальной линии, вы можете не получать 200 mousemove событий. Некоторые будут упущены. У меня нет проблем с этим.

Но если некоторые пиксельные движения пропущены так же, как мышь пересекает границу div, почему следует, что событие mouseout также должно быть пропущено? Когда браузер, наконец, снова начнет регистрировать позицию мыши (после внезапного быстрого движения), даже если мышь теперь находится за пределами поля, точка в том, что она была в коробке и больше не была. Так почему же тогда он не запускает событие mouseout?

Я просто не понимаю, почему это будет трудной проблемой для поставщиков браузеров. (Но я верю, что может быть веская причина, о которой я слишком глуп, чтобы думать.)

Я отправляю этот вопрос в основном из любопытства, но я надеюсь, что ответ может дать некоторое представление, которое могло бы помочь мне более эффективно решить проблему. Кроме того, приветствуются любые альтернативные обходные пути (которые быстрее, чем приведенные выше).

Ответ 1

Я знаю, что вам не нужен обходной путь, но вам не нужно проверять мышь x/y, чтобы узнать, есть ли у вас элемент или из него. Вы можете просто проверить элемент, из которого был запущен событие mousemove. Если вы поместите mousemove в документ, событие будет срабатывать от одного из его дочерних элементов, и вы можете сравнить этот элемент с вашим элементом, чтобы узнать, является ли он одним из его потомков.

Или вы можете подойти к дереву parentNode и остановиться, если найдете свой элемент. Затем вы знаете, что находитесь внутри элемента и все еще в нем, иначе вы достигнете документа, и вы выйдете.

Некоторые браузеры реализуют события mouseenter/mouseleave, которые, как я заметил, более точны, чем mouseout. Прототип и jQuery имеют обходное решение для браузеров, которые не реализуют эти новые события. Mouseleave не срабатывает из дочерних элементов, тогда как mouseout делает.

Ответ 2

Вы описываете перемещение мыши очень быстро. Когда вы остановились, указатель все еще находится на странице? То есть указатель мыши все еще витает над некоторой частью видимой веб-страницы?

Если он вышел наружу, тогда не обязательно ясно, что должен делать браузер. Событие mouseout должно иметь свойство relatedTarget, которое предназначено для ввода указателя мыши. Если указатель мыши уже находится за пределами области страницы, к нему не будет привязана ссылка.

Другими словами, когда мышь покидает область страницы, браузер перестает отслеживать ее и перестает сообщать о своей позиции. Если вы двигаете мышью достаточно быстро, с точки зрения браузера, мышь просто исчезла. Это произойдет, пока вы не вернете мышь обратно в ограничительную рамку просматриваемой страницы, где браузер знает, где она находится, и затем запускает все соответствующие действия на основе движения (например, мышь).

Ответ 3

Я столкнулся с этой проблемой несколько раз, и я пришел, чтобы принять эту проблему как факт жизни. Но, в зависимости от ваших потребностей, вы можете просто использовать CSS, как и я. Например, если я просто хочу показать/скрыть базовую базу элемента на другом элементе, который был завис, то CSS - это путь. Вот рабочий, надежный пример:

.large {
  width: 175px; height: 175px;
  position: absolute;
  border-radius: 100%;

  /*hide the glass by default*/
  top: -9999px;
  left: -9999px;
  opacity: 0;
  transition: opacity .2s ease-in-out;
  z-index: 100;
  pointer-events: none;
}

.small:hover + .large {
  opacity: 1;
}

http://codepen.io/tanduong/pen/aBMxyd

Ответ 4

  • Почему браузер не может надежно захватить событие mouseout? Если я могу с уверенностью сказать, когда мышь покинула div, используя вышеописанное решение, почему браузер не может это сделать?

    Думаю, ты сам ответил на это, когда сказал:

    По сравнению с тем, чтобы браузер делал все это изначально, выполнение вычислений при каждом перемещении пикселей немного связано с производительностью.

    Браузер не интерполирует между кадрами, поэтому, как вы заявили, он потребует гораздо больше ресурсов и памяти, поэтому может быть и не "исправлено".

  • Если некоторые перемещения пикселей пропущены так же, как мышь пересекает границу div, почему следует, что событие mouseout также должно быть пропущено? Когда браузер, наконец, снова начнет регистрировать позицию мыши (после внезапного быстрого движения), даже если мышь теперь находится за пределами поля, точка в том, что она была в коробке и больше не была. Так почему же тогда он не запускает событие mouseout?

    Я не знаю точно, но я не думаю, что это условие "это было и сейчас". Вместо этого он пересекает эту границу (если MouseX - ElemOffsetX= 1). Я согласен, это не имеет особого смысла, но может быть, потому что, если вы установите значение > 1, это вызовет событие несколько раз. В противном случае ему пришлось бы отслеживать события, которые не относятся к природе JS, видя, как он просто добавляет события в стек.


Что вы можете попробовать использовать jQuery mouseleave event. Это делает две вещи, что задерживает запуск события:

  • Он пересекает дерево DOM, чтобы увидеть, действительно ли он оставил элемент
  • Я думаю, что он реализует тайм-аут, который должен решить проблему интерполяции, которую вы заметили.

Ответ 5

Я нашел ваш вопрос и отсутствие других ясных ответов полезным, потому что он сказал мне, что мне нужно создать обходной путь. Что я использовал, используя идеи, представленные в вашем вопросе, и другие участники.

У меня такая же проблема, когда я использую jquery mouseleave elem.bind('mouseleave', data, mouseLeavesZone);

Проблема прерывистая и может быть связана с занятым ЦП на клиенте. Скажем, например, процессор занят где-то еще, когда мышь выходит из div. Тогда логично, что это может быть причиной ошибки. Согласен; это должно быть исправлено поставщиками браузеров.

См. http://jsfiddle.net/bgil2012/gWP5x/1/

(Помимо этого: моему JQuery-коду нужно использовать более старые методы jQuery, потому что он должен запускаться в Drupal 7, на котором выполняется jQuery 1.4, в это время и без применения исправлений).