Delegating the focus and blur events

转载自:http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html

Nowadays many JavaScripters are aware of the advantages of event delegation. Chris Heilmann and Dan Webb, among others, have discussed its advantages, and I’ve been using it as much as possible for about two years now.

Event delegation is especially useful in effects like dropdown menus, where lots of events on links may take place that can easily be handled at the root level (an <ol> or <ul> in this case).

There used to be one problem, though: although event delegation works fine for the mouse events, it does not work for the focus and blur events we need to make dropdown menus keyboard-accessible.

In the course of my ongoing event research, however, I found a way to delegate the focus and blur events, too. Maybe one of those frightfully clever JavaScript library authors will use this technique to shave off a few milliseconds of computing time

For all I know they’re already aware of this technique; but it was new to me so I publish it anyway.

Example.

What is event delegation?

For those unaware of it, let’s briefly discuss event delegation. As context I’ll use a dropdown menu.

When a user uses a mouse to steer through a dropdown menu, you want to capture all mouseover and mouseout events in order to see whether the latest user action should open or close a menu. (You also have to carefully distinguish between useful and useless events because Firefox, Safari and Opera still don’t support the mouseenter and mouseleave events, but that’s another story.)

The mouseover and mouseout events bubble up; that is, when a mouseover event on a link fires, the event “climbs” the DOM tree to see if any mouseover event handlers are defined on the ancestor nodes of the link. It first checks the link itself, then goes on to the containing <li>, followed by the <ol>, and all the way up to document or window level.

That means that it’s perfectly possible to define your generic onmouseover and onmouseout event handlers on the <ol>that forms the root element of the dropdown menu. When these events take place lower in the tree, event bubbling makes sure they eventually end up at the root element and can be handled there.

<ol id="dropdown">
    <li><a href="#">List item 1</a>
        <ol>
            <li><a href="#">List item 1.1</a></li>
            <li><a href="#">List item 1.2</a></li>
            <li><a href="#">List item 1.3</a></li>
        </ol>
    </li>
    [etc.]
</ol>

$('dropdown').onmouseover = handleMouseOver;
$('dropdown').onmouseout = handleMouseOut;
The great advantage of this trick is that you don’t need to set two event handlers on every link, which may save some browser memory.

The focus problem

This works fine. However, as soon as you want to make your dropdown keyboard-accessible, too, you run into a problem.

In theory, making a dropdown keyboard-accessible is easy: you just define event handlers for the focus and blur events, too. The problem is that these events do not bubble up. A focus or blur event on a link fires only on the link itself, and not on any ancestor element of the link.

This is an ancient rule. A few events, most motably focus, blur, and change, do not bubble up the document tree. The exact reasons for this have been lost in the mist of history, but part of the cause is that these events just don’t make sense on some elements. The user cannot focus on or change a random paragraph in any way, and therefore these events are just not available on these HTML elements. In addition, they do not bubble up.

Take the following situation:

<p id="testParagraph">
    Some text.
    <input id="testInput" />
</p>

$('testParagraph').onfocus = handleEventPar;
$('testInput').onfocus = handleEventInput;
When the user focuses on the input field, the `handleEventInput` function is executed. However, the event does not bubble up, so `handleEventPar` is not executed. In addition, it’s not possible to focus on the paragraph (except when it has a tabindex attribute), so `handleEventPar` is never executed.

Event capturing

Except when you use event capturing.

Event capturing is the reverse of event bubbling. When an event bubbles up it starts at the event target and then moves up the DOM tree. When an event is captured, it starts at the top of the DOM tree (usually the window or document object) and then moves down to the event target.

One of the most curious conclusions of my event research is that when you define event handlers in the capturing phase the browser executes any and all event handlers set on ancestors of the event target whether the given event makes sense on these elements or not.

So let’s take the same example as above, but now with addEventListener in the capturing phase. (You define it on the capturing phase by setting the last argument to true.)

<p id="testParagraph">
    Some text.
    <input id="testInput" />
</p>

$('testParagraph').onfocus = handleEventPar;
$('testInput').onfocus = handleEventInput;
$('testParagraph').addEventListener('focus',handleEventPar,true);
$('testInput').addEventListener('focus',handleEventInput,true);

Now if the user focuses on the input, the event starts at the window or document level and then moves down to the input. On the way it encounters the onfocus event handler on the paragraph and executes it, even though the event does not make sense on a paragraph.

As a result, handleEventPar is executed, and then handleEventInput.

IE

Unfortunately IE does not support event capturing. However, it supports the focusin and focusout events that, contrary to focus and blur, do bubble up. If we use these events to get IE on board, we’re ready to sail.

Delegating the focus and blur events

Therefore the conclusion must be that onfocus and onblur event handlers may be delegated just as onmouseover and onmouseout are by registering them in the capturing phase. In order to properly delegate all events for a keyboard-accessible dropdown menu you do:

<ol id="dropdown">
    <li><a href="#">List item 1</a>
        <ol>
            <li><a href="#">List item 1.1</a></li>
            <li><a href="#">List item 1.2</a></li>
            <li><a href="#">List item 1.3</a></li>
        </ol>
    </li>
    [etc.]
</ol>

$('dropdown').onmouseover = handleMouseOver;
$('dropdown').onmouseout = handleMouseOut;
$('dropdown').onfocusin = handleMouseOver;
$('dropdown').onfocusout = handleMouseOut;
$('dropdown').addEventListener('focus',handleMouseOver,true);
$('dropdown').addEventListener('blur',handleMouseOut,true);
Now you've succesfully delegated the focus and blur events.

Example.

Further questions

The fact that the capturing phase of the event works different from the bubbling phase raises a few questions, which I might answer at a later date:

  • Shouldn’t event bubbling and event capturing work exactly the same way? That is, shouldn’t both phases either execute all event handlers they find or use the old “only on sensible elements” rule?
  • Isn’t it time to allow all events to bubble up?

Note: In the case of focus and blur, it might make sense for the
events not to bubble up. After all, if a user focuses on a link or
form field, you don’t want any generic window.onfocus to fire, because
on the window that event has a totally different function: it fires
when the user focuses on the entire browser window.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值