When an event occurs on a page, an entire hierarchy
of DOM elements gets a chance to handle the event. Consider a page
model like this:
<div class="foo">
DOM elementshierarchy<span class="bar">
<a href="http://www.example.com/">
The quick brown fox jumps over the lazy dog.
</a>
</span>
<p>
How razorback-jumping frogs can level six piqued gymnasts!
</p>
</div>
We then visualize the code as a set of nested elements as shown in the following figure:
For any event, there are
multiple elements that could logically be responsible for reacting. When
the link on this page is clicked, for example, the <div>, <span>, and <a>
all should get the opportunity to respond to the click. After all, the
three are all under the user's mouse cursor at the time. The <p> element, on the other hand, is not part of this interaction at all.
One strategy for allowing multiple elements to respond to a click is called event capturing.
With event capturing, the event is first given to the most
all-encompassing element, and then to successively more specific ones.
In our example, this means that first the <div> gets passed the event, then the <span>, and finally the <a>.
Technically, in browser implementations of event capturing, specific elements register to listen for events that occur among their descendants. The approximation provided here is close enough for our needs.
The opposite strategy is called event bubbling. The event gets sent to the most specific element, and after this element has an opportunity to react, the event bubbles up to more general elements. In our example, the <a> would be handed the event first, and then the <span> and <div> in that order.
Unsurprisingly, different
browser developers originally decided on different models for event
propagation. The DOM standard that eventually developed thus specified
that both strategies should be used: first the event is captured from general elements to specific ones, and then the event bubbles back up to the top of the DOM tree. Event handlers can be registered for either part of the process.
Not all browsers have been
updated to match this new standard, and in those that support capturing
it typically must be specifically enabled. To provide cross-browser
consistency, therefore, jQuery always registers event handlers for the
bubbling phase of the model. We can always assume that the most specific
element will get the first opportunity to respond to any event.
Side effects of event bubbling
Event bubbling can cause unexpected behavior, especially when the wrong element responds to a mouseover or mouseout. Consider a mouseout event handler attached to the <div> in our example. When the user's mouse cursor exits the <div>, the mouseout
handler is run as anticipated. Since this is at the top of the
hierarchy, no other elements get the event. On the other hand, when the
cursor exits the <a> element, a mouseout event is sent to that. This event will then bubble up to the <span> and then to the <div>,
firing the same event handler. This bubbling sequence is likely not
desired; for the buttons in our style switcher example, it could mean
the highlight was turned off prematurely.
The .hover()
method is aware of these bubbling issues, and when we use that method to
attach events, we can ignore the problems caused by the wrong element
getting a mouseover or mouseout event. This makes .hover() a very attractive alternative to binding the individual mouse events.
If action only needs to be taken when the mouse enters or leaves an element, but not both, we can bind jQuery's mouseenter and mouseleave events, which also circumvent bubbling concerns. These events are paired so often, however, that .hover() is generally the right choice.
The mouseout scenario just described illustrates the need to constrain the scope of an event. While .hover()
handles this specific case, we will encounter other situations in which
we need to limit an event spatially (preventing the event from being
sent to certain elements) or temporally (preventing the event from being
sent at certain times).