Источник: http://learn.javascript.ru/mousemove-events
В этой главе мы рассмотрим события, возникающие при движении мыши над элементами.
Событие mouseover
происходит, когда мышь появляется над элементом, а mouseout
— когда уходит из него.
В этих событиях мышь переходит с одного элемента на другой. Оба этих элемента можно получить из свойств объекта события.
event.target
(IE: srcElement
).event.relatedTarget
(IE: fromElement
)event.target
(IE: srcElement
).event.relatedTarget
(IE: toElement
).Как вы видите, спецификация W3C объединяет fromElement
и toElement
в одно свойство relatedTarget
, которое работает, как fromElement
для mouseover
и как toElement
для mouseout
.
В IE это свойство можно поставить так:
function fixRelatedTarget(e) {
if (!e.relatedTarget) {
if (e.type == 'mouseover') e.relatedTarget = e.fromElement;
if (e.type == 'mouseout') e.relatedTarget = e.toElement;
}
}
Значение relatedTarget
(toElement/fromElement
) может быть null
Такое бывает, например, когда мышь приходит из-за пределов окна у mouseover будет relatedTarget = null
.
Событие mousemove срабатывает при передвижении мыши. Но это не значит, что каждый пиксель экрана порождает отдельное событие!
События mousemove
и mouseover/mouseout
срабатывают с такой частотой, с которой это позволяет внутренний таймер браузера.
Это означает, что если вы двигаете мышью очень быстро, то DOM-элементы, через которые мышь проходит на большой скорости, могут быть пропущены.
Несмотря на некоторую концептуальную странность такого подхода, он весьма разумен. Хотя браузер и может пропустить промежуточные элементы, он гарантирует, что если уж мышь зашла на элемент (сработало событие mouseover
), то при выходе с него сработает и mouseout
. Не может быть mouseover
без mouseout
и наоборот.
Так что эти события позволяют надёжно обрабатывать заход на элемент и уход с него.
Представьте ситуацию — курсор зашел на элемент. Сработал mouseover
на нём. Потом курсор идёт на дочерний… И, оказывается, на элементе-родителе при этом происходит mouseout
! Как будто курсор с него ушёл, хотя он всего лишь перешёл на потомка.
Это происходит потому, что согласно браузерной логике, курсор мыши может быть только над одним элементом — самым глубоким в DOM (и верхним по z-index
).
Так что если он перешел на потомка — значит ушёл с родителя.
Получается, что при переходе на потомка курсор уходит mouseout
с родителя, а затем тут же переходит mouseover
на него. Причем возвращение происходит за счёт всплытия mouseover
с потомка.
События mouseenter/mouseleave
похожи на mouseover/mouseout
. Они тоже срабатывают, когда курсор заходит на элемент и уходит с него, но с двумя отличиями.
mouseenter
, а затем — неважно, куда он внутри него переходит, mouseleave
будет, когда курсор окажется за пределами элемента.События mouseenter/mouseleave
не всплывают.
Для браузеров, в которых нет поддержки этих событий, можно повесить обработчик на mouseover/mouseout
, а лишние события — фильтровать.
При mouseout
можно получить элемент, на который осуществляется переход (e.relatedTarget
), и проверить — является ли новый элемент потомком родителя. Если да — мышь с родителя не уходила, игнорировать это событие.
При mouseover
— аналогичным образом проверить, мышь «пришла» с потомка? Если с потомка, то это не настоящий переход на родителя, игнорировать.
Посмотрим, как это выглядит, на примере кода:
<div style="padding:10px; margin:10px; border: 2px solid blue" id="outer">
<p style="border: 1px solid green">
Обработчики mouseover/mouseout стоят на синем родителе.
</p>
<blockquote style="border: 1px solid red">
..Но срабатывают и при любых переходах по его потомкам!
</blockquote>
</div>
<b id="info">Тут будет информация о событиях.</b>
<script>
var outer = document.getElementById('outer')
var info = document.getElementById('info');
outer.onmouseout = function(e) {
e = e || event;
var target = e.target || e.srcElement;
info.innerHTML = e.type+', target:'+target.tagName;
};
outer.onmouseover = function(e) {
e = e || event;
var target = e.target || e.srcElement;
info.innerHTML = e.type+', target:'+target.tagName;
};
</script>
У mouseover
, mousemove
, mouseout
есть следующие особенности:
mouseove
и mouseout
— единственные, у которых есть вторая цель: relatedTarget
(toElement/fromElement
в IE).mouseout
срабатывает, когда мышь уходит с родительского элемента на дочерний. Используйте mouseenter/mouseleave
или фильтруйте их, чтобы избежать излишнего реагирования.mouseover
, mousemove
, mouseout
могут пропускать промежуточные элементы. Мышь может моментально возникнуть над потомком, миновав при этом его родителя.События mouseleave/mouseenter
поддерживаются не во всех браузерах, но их можно эмулировать, отсеивая лишние срабатывания mouseover/mouseout
.