[whatwg] Adding mouseenter and mouseleave events

Bjoern Hoehrmann derhoermi at gmx.net
Sat May 10 19:23:25 PDT 2008

* Magnus Kristiansen wrote:
>Mouseover/out events will trigger when elements contained inside the  
>EventTarget are hovered, and then bubble up. This is contrary to the most  
>obvious interpretation, as you are still inside (over) the targeted  
>element. IE supports two events, mouseenter[1] and mouseleave[2], which  
>solve this problem by not bubbling.

This actually has little to do with the bubbling behavior but with the
fact that the mouse events are always targeted at the most deeply nested
element, while mouseenter and mouseleave are targeted at all ancestors
not shared by the two relevant event targets. So moving the cursor from
$a to $b you get (in this order):

  mouseleave to `$a/ancestor-or-self::* except $b/ancestor-or-self::*`
  mouseenter to `$b/ancestor-or-self::* except $a/ancestor-or-self::*`

in, respectively, reverse document order and document order. This means
for example that you get no mouseleave event when moving from an element
to one of its descendants, and no mouseenter event when moving from an
element to one of its ancestors. Calling them mouse-joins-descendant-or-
self-axis and mouse-leaves-descendant-or-self-axis would be clearer.

>It is possible to work around the problem by using target/relatedTarget  
>and walking up the DOM tree. However, this requires extra code for every  
>event handler. Besides, these events were often not meant to be generated  
>in the first place, by the intent of the author.

You can simply generate your own events, using document.createEvent(),
and thereby avoid writing code for each handler. You can also use a
general purpose wrapper for your handlers that is registered instead
and decides whether to call the wrapped handler to a similar effect.
For the former you could simply register this brevity-optimized handler
on the document for the capture phase for both mouseover and mouseout:

  function (evt) {
    var t = [], n = 'mouseenter', b = evt.relatedTarget;
    for (var x = evt.target; x; x = x.parentNode)
      if (!b || !((x.compareDocumentPosition(b) || 0x10) & 0x10))

    if (evt.type == 'mouseout') { n = 'mouseleave'; t = t.reverse(); }

    while (t.length) {
      var ne = document.createEvent("MouseEvents");
      ne.initMouseEvent(n, false, evt.cancelable, evt.view,
        evt.detail, evt.screenX, evt.screenY, evt.clientX,
        evt.clientY, evt.ctrlKey, evt.altKey, evt.shiftKey,
        evt.metaKey, evt.button, evt.relatedTarget);
      t.pop().dispatchEvent(ne); } }

In browsers that support the features used above the effect would be
as if they support the two additional events. The code for the other
method would be simpler, as you can see in several script libraries
that support these events in some way.

>I have no statistics for how often mouseover/out are used with and without  
>intent of bubbling, but the anecdotal evidence from my own experience has  
>never found me wanting it.

Not caring about bubbled up event objects is quite different from the
mouseenter and mouseleave events, you can simply check the .eventPhase
to ignore the former and that you have to do so is a limitation of the
registration system. The mouseenter and mouseleave events have their
own pseudo-bubbling behavior which might let you forget the benefits.
Björn Höhrmann · mailto:bjoern at hoehrmann.de · http://bjoern.hoehrmann.de
Weinh. Str. 22 · Telefon: +49(0)621/4309674 · http://www.bjoernsworld.de
68309 Mannheim · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/ 

More information about the whatwg mailing list