# Moving the mouse: mouseover/out, mouseenter/leave Let's dive into more details about events that happen when the mouse moves between elements. ## Events mouseover/mouseout, relatedTarget The `mouseover` event occurs when a mouse pointer comes over an element, and `mouseout` -- when it leaves. ![](mouseover-mouseout.svg) These events are special, because they have property `relatedTarget`. This property complements `target`. When a mouse leaves one element for another, one of them becomes `target`, and the other one - `relatedTarget`. For `mouseover`: - `event.target` -- is the element where the mouse came over. - `event.relatedTarget` -- is the element from which the mouse came (`relatedTarget` -> `target`). For `mouseout` the reverse: - `event.target` -- is the element that the mouse left. - `event.relatedTarget` -- is the new under-the-pointer element, that mouse left for (`target` -> `relatedTarget`). ```online In the example below each face and its features are separate elements. When you move the mouse, you can see mouse events in the text area. Each event has the information about both `target` and `relatedTarget`: [codetabs src="mouseoverout" height=280] ``` ```warn header="`relatedTarget` can be `null`" The `relatedTarget` property can be `null`. That's normal and just means that the mouse came not from another element, but from out of the window. Or that it left the window. We should keep that possibility in mind when using `event.relatedTarget` in our code. If we access `event.relatedTarget.tagName`, then there will be an error. ``` ## Skipping elements The `mousemove` event triggers when the mouse moves. But that doesn't mean that every pixel leads to an event. The browser checks the mouse position from time to time. And if it notices changes then triggers the events. That means that if the visitor is moving the mouse very fast then some DOM-elements may be skipped: ![](mouseover-mouseout-over-elems.svg) If the mouse moves very fast from `#FROM` to `#TO` elements as painted above, then intermediate `
` (or some of them) may be skipped. The `mouseout` event may trigger on `#FROM` and then immediately `mouseover` on `#TO`. That's good for performance, because if there may be many intermediate elements. We don't really want to process in and out of each one. On the other hand, we should keep in mind that the mouse pointer doesn't "visit" all elements along the way. It can "jump". In particular, it's possible that the pointer jumps right inside the middle of the page from out of the window. In that case `relatedTarget` is `null`, because it came from "nowhere": ![](mouseover-mouseout-from-outside.svg) ```online You can check it out "live" on a teststand below. Its HTML has two nested elements: the `
` is inside the `
`. If you move the mouse fast over them, then maybe only the child div triggers events, or maybe the parent one, or maybe there will be no events at all. Also move the pointer into the child `div`, and then move it out quickly down through the parent one. If the movement is fast enough, then the parent element is ignored. The mouse will cross the parent element without noticing it. [codetabs height=360 src="mouseoverout-fast"] ``` ```smart header="If `mouseover` triggered, there must be `mouseout`" In case of fast mouse movements, intermediate elements may be ignores, but one thing we know for sure: elements can be only skipped as a whole. If the pointer "officially" entered an element with `mouseover`, then upon leaving it we always get `mouseout`. ``` ## Mouseout when leaving for a child An important feature of `mouseout` -- it triggers, when the pointer moves from an element to its descendant. Visually, the pointer is still on the element, but we get `mouseout`! ![](mouseover-to-child.svg) That looks strange, but can be easily explained. **According to the browser logic, the mouse cursor may be only over a *single* element at any time -- the most nested one and top by z-index.** So if it goes to another element (even a descendant), then it leaves the previous one. Please note an important detail. The `mouseover` event on a descendant bubbles up. So, if the parent element has such handler, it triggers. ![](mouseover-bubble-nested.svg) ```online You can see that very well in the example below: `
` is inside the `
`. There are handlers on the parent that listen for `mouseover/out` events and output their details. If you move the mouse from the parent to the child, you see two events: `mouseout [target: parent]` (left the parent) and `mouseover [target: child]` (came to the child, bubbled). [codetabs height=360 src="mouseoverout-child"] ``` When we move from a parent element to a child, then two handlers trigger on the parent element: `mouseout` and `mouseover`: ```js parent.onmouseout = function(event) { /* event.target: parent element */ }; parent.onmouseover = function(event) { /* event.target: child element (bubbled) */ }; ``` If the code inside the handlers doesn't look at `target`, then it might think that the mouse left the `parent` element, and then came back over it. But it's not the case! The mouse never left, it just moved to the child element. If there's some action upon leaving the element, e.g. animation runs, then such interpretation may bring unwanted side effects. To avoid it, we can check `relatedTarget` and, if the mouse is still inside the element, then ignore such event. Alternatively we can use other events: `mouseenter` и `mouseleave`, that we'll be covering now, as they don't have such problems. ## Events mouseenter and mouseleave Events `mouseenter/mouseleave` are like `mouseover/mouseout`. They trigger when the mouse pointer enters/leaves the element. But there are two important differences: 1. Transitions inside the element, to/from descendants, are not counted. 2. Events `mouseenter/mouseleave` do not bubble. These events are extremely simple. When the pointer enters an element -- `mouseenter` triggers. The exact location of the pointer inside the element or its descendants doesn't matter. When the pointer leaves an element -- `mouseleave` triggers. ```online This example is similar to the one above, but now the top element has `mouseenter/mouseleave` instead of `mouseover/mouseout`. As you can see, the only generated events are the ones related to moving the pointer in and out of the top element. Nothing happens when the pointer goes to the child and back. Transitions between descendants are ignores [codetabs height=340 src="mouseleave"] ``` ## Event delegation Events `mouseenter/leave` are very simple and easy to use. But they do not bubble. So we can't use event delegation with them. Imagine we want to handle mouse enter/leave for table cells. And there are hundreds of cells. The natural solution would be -- to set the handler on `` and process events there. But `mouseenter/leave` don't bubble. So if such event happens on `
`, then only a handler on that `` is able to catch it. Handlers for `mouseenter/leave` on `` only trigger when the pointer enters/leaves the table as a whole. It's impossible to get any information about transitions inside it. So, let's use `mouseover/mouseout`. Let's start with simple handlers that highlight the element under mouse: ```js // let's highlight an element under the pointer table.onmouseover = function(event) { let target = event.target; target.style.background = 'pink'; }; table.onmouseout = function(event) { let target = event.target; target.style.background = ''; }; ``` ```online Here they are in action. As the mouse travels across the elements of this table, the current one is highlighted: [codetabs height=480 src="mouseenter-mouseleave-delegation"] ``` In our case we'd like to handle transitions between table cells `
`: entering a cell and leaving it. Other transitions, such as inside the cell or outside of any cells, don't interest us. Let's filter them out. Here's what we can do: - Remember the currently highlighted `` in a variable, let's call it `currentElem`. - On `mouseover` -- ignore the event if we're still inside the current ``. - On `mouseout` -- ignore if we didn't leave the current ``. Here's an example of code that accounts for all possible situations: [js src="mouseenter-mouseleave-delegation-2/script.js"] ```online Here's the full example with all details: [codetabs height=380 src="mouseenter-mouseleave-delegation-2"] Try to move the cursor in and out of table cells and inside them. Fast or slow -- doesn't matter. Only `` as a whole is highlighted, unlike the example before. ``` ## Summary We covered events `mouseover`, `mouseout`, `mousemove`, `mouseenter` and `mouseleave`. These things are good to note: - A fast mouse move may skip intermediate elements. - Events `mouseover/out` and `mouseenter/leave` have an additional property: `relatedTarget`. That's the element that we are coming from/to, complementary to `target`. Events `mouseover/out` trigger even when we go from the parent element to a child element. The browser assumes that the mouse can be only over one element at one time -- the deepest one. Events `mouseenter/leave` are different in that aspect: they only trigger when the mouse comes in and out the element as a whole. Also they do not bubble.