Delegating the focus and blur events

 

这是Peter-Paul Koch写的一篇文章

Delegating the focus and blur events

原文出自:http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html

 

我的翻译:

    委托focus和blur事件

 现在很多JavaScript编写者们都已经了解到了事件委托的好处。Chris Helimann和Dan Webb曾论述过它的优势,我也在最近两年经常使用到它。

 事件委托在一些情况下尤其有用,比如下拉菜单中,当我们直接在根节点上(一个<ol>或<ul>)注册事件将会比为每个链接注册事件简单很多。

  但这里有个问题:尽管事件委托对鼠标事件来说能有效使用,但是对于focus和blur事件会失去作用,比如当我们制作一个键盘可访问的下来菜单时。

   在我对事件研究的过程中,我发现了一种可以对focus和blur事件进行委托的方法。或许一个非常聪明的JavaScript库作者将会使用这种技术来减少几毫秒的计算时间。

  就我所知,他们已经意识到了这种方法,但是对我来说,是一种新方法,所以我把它发布出来了。

例子:

  什么是事件委托?

这里简单描述一下,为了那些不知道的人,我用一个下拉菜单来说明。

当一个用户使用鼠标来操控下拉菜单时,你会想捕获所有鼠标悬停和鼠标离开事件,为了知道用户的最后一个动作是要关闭还是打开菜单。(你也必须注意区分有用和无用的事件,因为firefox,safari,opera仍然不支持mouseenter和mouseleave事件,但这是另一个故事了)。

 

mouseover和mouseout事件都会冒泡;当mouseover事件在一个链接上触发时,这个事件将会沿着DOM树向上,去查找这个节点的那些祖先元素有没有定义mouseover事件。它首先会检查它自身,然后检查包含它的<li>,接着就是<ol>等直到document或者window。

这意味着你可以定义 onmouseover和onmouseout事件函数在像下拉菜单<ol>这样的根元素上,当事件在DOM树深层元素上触发时,事件冒泡可以冒泡到这个根元素上并执行函数。

<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;

这样的好处是你不必为每个a链接注册两个函数,还能节省一点浏览器内存。

focus的问题

这个事件本身没问题,但是,一旦你想让你的下拉菜单也能被键盘访问,你就会遇到一个问题。

理论上,做出一个键盘可访问的下拉菜单很容易:你只需要为focus和blur事件注册函数。问题是这两个事件不会冒泡。一个链接的focus或blur事件只会触发它自身,不会触发它的祖先元素。

这是一个古老的规则,有一些事件,比如focus、blur、change都不会冒泡。准确的原因在发展历史中一直都模糊不清。但是部分原因是因为这些事件对有些元素不会起作用。用户不能让一个段落获得焦点或者发生改变。因此,这些HTML元素不能设置这些事件。此外,它们也不能冒泡。

 

请看下面这个例子:

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

$('testParagraph').onfocus = handleEventPar;
$('testInput').onfocus = handleEventInput;

当用户在文本框上获得焦点时,handlerEventInput函数会被执行,但是,这个事件不会冒泡,所以handlerEventPar不会执行。另外,不可能对一个段落获得焦点(除非他又一个tabindex的属性),所以handlerEvenPar绝对不会执行。

事件捕获

解决这个问题除非你用事件捕获。

事件捕获恰好和事件冒泡相反。事件冒泡是从目标元素向上沿着DOM树移动。当一个事件被捕获时,它从DOM树的顶层开始(通常是window和document)向下传播一直到目标元素。



在我的事件研究中让我最困惑的结果是,当你在事件捕获阶段定义事件函数时,浏览器将会在目标元素的祖先元素上执行所有事件函数,不管这个事件对这个目标元素能不能作用。

来让我们看一些例子,我们用addEventListner,并在事件捕获阶段调用事件处理程序。(把最后一个参数设置为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);

现在如果你让input获得焦点,这个事件会从window或者document开始向下移动到input,传播过程中,将会途径段落,并在段落上调用这个函数,尽管onfocus事件对段落本来是不起作用的。
因此,handleEventPar会执行,然后handleEventInput再执行。



IE

不幸的是,IE并不支持事件捕获,但它支持另外两个focusin和foucusout事件,这两个事件会冒泡。如果我们在IE中用这两个事件作为上船的准备,我们就可以准备航行了。

委托focus和blur事件


因此,结论就是,要想让focus和blur事件委托像mouseover和mouseout那样,就必须在捕获阶段,为他们注册处理程序。为了正确的给一个可键盘访问的下拉菜单进行事件委托,你应该这样做:

<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);

这样你就可以成功的委托blur和focus事件了。

 

posted on 2015-10-30 20:52  daydreamer_xyf  阅读(169)  评论(0)    收藏  举报

导航