首页 > 开发 > 综合 > 正文

关于DOM事件模型的两件事

2020-09-19 11:10:10
字体:
来源:转载
供稿:网友
  • 事件捕捉(event capture)的实现问题
  • ie的高级事件处理模型的问题

一、事件捕捉(event capture)的实现问题

首先在说这件事前,先感谢一下realazy。 

w3c dom level2的事件模型规范中,事件在dom树中的传播过程(从根节点到目标节点)被分为了两个阶段:捕捉(capture)和冒泡(bubbling)。下面这个图能大概的说明整个过程:

关于dom事件模型的两件事_网页设计CuoXin.com整理转载
(from w3c)

如果想创建一个捕捉事件,在支持w3c 事件模型的浏览器中,将addeventlistener的第三个参数设为true就好了。例如:

document.getelementbyid('foo').addeventlistener('click',function(){alert('hello, world!');},true);

前一阵因为想弄懂事件捕捉,所以做了点小实验,分别在firefox 2、safari 3 on windows和opera 9上实践了事件捕捉(当然,因为ie不支持事件捕捉,所以…),实验的原理见下图:

关于dom事件模型的两件事_网页设计CuoXin.com整理转载

id为div1和div2的两个元素都被委派了捕捉阶段的事件处理函数,这样:

  • 当点击#div1(蓝色区域)时,应该会alert出”div1′
  • 当点击#div2(黄色区域)时,应该会先alert出”div1′,再alert出”div2′,因为在事件捕捉阶段,事件是从根元素向下传播的,#div1是#div2的父元素,自然绑定在#div1上的click事件也会先于#div2上的click事件被执行。

然而,以上的设想只试用于firefox 2和safari 3 on windows,在opera 9中,事情会变成这样:

  • 当点击#div1(蓝色区域)时,什么都不会发生
  • 当点击#div2(黄色区域)时,会alert出”div1′,随后什么都不会再发生

可以看出,在opera 9中,目标元素(targetelement)的click事件没有被执行。通过realazy(orz…)的指点,找到了这篇文章:《event capture explained》,发现,

来opera中的实现才是正确的。此文中有一段话如是说
the dom spec states that capturing events should not fire on target, because the idea of a capturing event is to detect events before they reach their targets. because of bugs in gecko and safari, web content that is tested mostly with firefox or other gecko-based browsers sometimes expects capturing listeners to fire on target. such content will fail in opera 7, 8 and current releases of 9 because of its correct implementation of the standard.

大意是说:dom规范中陈述了捕捉型的事件不应该在目标元素上被执行,因为捕捉型事件的用意就是为了监测到达目标元素之前的事件。firefox和safari的实现都是带有bug的。

再来看看w3c的dom events规范中的原话
a capturing eventlistener will not be triggered by events dispatched directly to the eventtarget upon which it is registered.

所以,在整个事件传播中,被执行的顺序是:

  1. 父元素中所有的捕捉型事件(如果有)自上而下地执行
  2. 目标元素的冒泡型事件(如果有)
  3. 父元素中所有的冒泡型事件(如果有)自下而上地执行

在了解了这些后,也许还是不要使用事件捕捉为妙,至少暂时不要。

|||

二、ie的高级事件处理模型的问题

重复绑定

ie下没有addeventlistener,但是也有自己的attachevent,即所谓的microsoft model。二者的实现基本相同只是attachevent的第一个参数(事件类型)需要加”on”,而addeventlistener不用,另外attachevent因为不支持事件捕捉,所以也没有第三个参数。

然而,attachevent还有一个很要命的问题:重复绑定事件。(这是从ppk on javascript中学到的)

一个例子:

function sayhello(){
alert('hello, world!');
}
// w3c model
$('div1').addeventlistener('click', sayhello, false);
$('div1').addeventlistener('click', sayhello, false);
// microsoft model
$('div1').attachevent('onclick', sayhello);
$('div1').attachevent('onclick', sayhello);

在w3c模型中,相同事件处理函数的绑定会被忽略,也就是说第二个$('div1').addeventlistener('click', sayhello, false);会被忽略。

而在microsoft模型中,第二个$('div1').attachevent('onclick', sayhello);同样会被执行,所以,当你点击#div1的时候,alert框会弹出来两次。更有甚者,在detachevent时候,也同样要detachevent两次才能彻底把sayhello从#div1的click事件中删除。

为什么不继续使用alertid()了?

这是因为ie的事件模型的另一个缺陷,在alertid中,使用了this关键字来指代被委派了该事件处理函数的元素,这样,在w3c模型中,alertid中的this指代了#div1或者#div2。

但是在microsoft模型中,缺少了对this的支持后,this.id就会变为undefined,因为这时候this指代了window对象。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表