Движение мыши: mouseover/out, mouseenter/leave
В этой главе мы более подробно рассмотрим события, возникающие при движении указателя мыши над элементами страницы.
События mouseover/mouseout, relatedTarget
Событие mouseover происходит в момент, когда курсор оказывается над элементом, а событие mouseout – в момент, когда курсор уходит с элемента.
Эти события являются особенными, потому что у них имеется свойство relatedTarget . Оно «дополняет» target . Когда мышь переходит с одного элемента на другой, то один из них будет target , а другой relatedTarget .
Для события mouseover :
- event.target – это элемент, на который курсор перешёл.
- event.relatedTarget – это элемент, с которого курсор ушёл ( relatedTarget → target ).
Для события mouseout наоборот:
- event.target – это элемент, с которого курсор ушёл.
- event.relatedTarget – это элемент, на который курсор перешёл ( target → relatedTarget ).
В примере ниже каждое лицо и его черты – отдельные элементы. При движении указателя по этим элементам в текстовом поле отображаются происходящие события.
Каждое из них содержит информацию о target и relatedTarget :
Свойство relatedTarget может быть null .
Это нормально и означает, что указатель мыши перешёл не с другого элемента, а из-за пределов окна браузера. Или же, наоборот, ушёл за пределы окна.
Следует держать в уме такую возможность при использовании event.relatedTarget в своём коде. Если, например, написать event.relatedTarget.tagName , то при отсутствии event.relatedTarget будет ошибка.
Пропуск элементов
Событие mousemove происходит при движении мыши. Однако, это не означает, что указанное событие генерируется при прохождении каждого пикселя.
Браузер периодически проверяет позицию курсора и, заметив изменения, генерирует события mousemove .
Это означает, что если пользователь двигает мышкой очень быстро, то некоторые DOM-элементы могут быть пропущены:
Если курсор мыши передвинуть очень быстро с элемента #FROM на элемент #TO , как это показано выше, то лежащие между ними элементы
Это хорошо с точки зрения производительности, потому что если промежуточных элементов много, вряд ли мы действительно хотим обрабатывать вход и выход для каждого.
С другой стороны, мы должны иметь в виду, что указатель мыши не «посещает» все элементы на своём пути. Он может и «прыгать».
В частности, возможно, что указатель запрыгнет в середину страницы из-за пределов окна браузера. В этом случае значение relatedTarget будет null , так как курсор пришёл «из ниоткуда»:
Вы можете проверить это «вживую» на тестовом стенде ниже.
В его HTML есть два элемента,
Также попробуйте поставить курсор на внутренний элемент, а затем очень быстро сделайте движение мышкой вниз через внешний элемент. Если у вас получится достаточно быстро, то на родительском элементе не будет сгенерировано никаких событий. То есть, мышь пройдёт через внешний элемент, не замечая его.
Несмотря на то, что при быстрых переходах промежуточные элементы могут игнорироваться, в одном мы можем быть уверены: элемент может быть пропущен только целиком.
Если указатель «официально» зашёл на элемент, то есть было событие mouseover , то при выходе с него обязательно будет mouseout .
Событие mouseout при переходе на потомка
Важная особенность события mouseout – оно генерируется в том числе, когда указатель переходит с элемента на его потомка.
То есть, визуально указатель всё ещё на элементе, но мы получим mouseout !
Это выглядит странно, но легко объясняется.
По логике браузера, курсор мыши может быть только над одним элементом в любой момент времени – над самым глубоко вложенным и верхним по z-index.
Таким образом, если курсор переходит на другой элемент (пусть даже дочерний), то он покидает предыдущий.
Обратите внимание на важную деталь.
Событие mouseover , происходящее на потомке, всплывает. Поэтому если на родительском элементе есть такой обработчик, то оно его вызовет.
Вы можете наглядно увидеть это в примере ниже:
При переходе мышью с внешнего элемента на внутренний, вы увидите сразу два события: mouseout [target: parent] (ушли с родителя) и mouseover [target: child] (перешли на потомка, событие всплыло).
При переходе с родителя элемента на потомка – на родителе сработают два обработчика: и mouseout и mouseover :
Если код внутри обработчиков не смотрит на target , то он подумает, что мышь ушла с элемента parent и вернулась на него обратно. Но это не так! Мышь никуда не уходила, она просто перешла на потомка.
Если при уходе с элемента что-то происходит, например, запускается анимация, то такая интерпретация происходящего может давать нежелательные побочные эффекты.
Чтобы этого избежать, можно смотреть на relatedTarget и, если мышь всё ещё внутри элемента, то игнорировать такие события.
Или же можно использовать другие события: mouseenter и mouseleave , которые мы сейчас изучим, с ними такая проблема не возникает.
События mouseenter и mouseleave
События mouseenter/mouseleave похожи на mouseover/mouseout . Они тоже генерируются, когда курсор мыши переходит на элемент или покидает его.
Но есть и пара важных отличий:
- Переходы внутри элемента, на его потомки и с них, не считаются.
- События mouseenter/mouseleave не всплывают.
События mouseenter/mouseleave предельно просты и понятны.
Когда указатель появляется над элементом – генерируется mouseenter , причём не имеет значения, где именно указатель: на самом элементе или на его потомке.
Событие mouseleave происходит, когда курсор покидает элемент.
Вот тот же пример, что и выше, но на этот раз на верхнем элементе стоят обработчики mouseenter/mouseleave вместо mouseover/mouseout .
Как вы сами можете увидеть, генерируются только события, связанные с движением курсора относительно верхнего
Делегирование событий
События mouseenter/leave просты и легки в использовании. Но они не всплывают. Таким образом, мы не можем их делегировать.
Представьте ситуацию, когда мы хотим обрабатывать события, сгенерированные при движении курсора по ячейкам таблицы. И в таблице сотни ячеек.
Очевидное решение – определить обработчик на родительском элементе
| , то только обработчик на | может поймать его. Обработчики событий mouseenter/leave на
не совсем понятно, поставь сюда display:»block»; Тут лучше через анонимную функцию сделать для каждого события свой обработчик, чем определять один, где уже идет распределение. Меньше кода будет Добавлено через 1 минуту Не работает mouseover() Событие MouseOver для пункта меню Нужно на событие mouseover на линк (without image) выдать сообщение типа ‘ALT’ Не работает VisualState x:Name=»MouseOver» в TabControl Источник глюк с mouseoverИнтересный глюк. Или не глюк? Смена дефолтного пути Mouseover/out Глюк или не глюк в Siglent SDS 1192CML. Изменить mouseover Странное решение — борьба с глюком через перезагрузку )), если я это вообще правильно понял. Можно код посмотреть? Это решаемые проблемы.
Жаль, что не видно визуально, кто там кого перекрывает. Было б легче ответить. Добавлено через 1 минуту не даст ничего. Там из-за особенностей визуализации события mouseout навешаны на несколько совершенно разных div-ов, и событие происходит многократно, при выходе из одного, потом из другого, когда курсор быстро пересекает все эти элементы. И на каком-то этапе заглючивается. Добавлено через 11 минут Источник |