Javascript - 事件顺序

时间: 2018-01-08阅读: 834标签: js事件

Netscape 4 只支持事件捕获,Explorer只支持事件冒泡。Netscape 6和 Konqueror冒泡和捕获均支持,但Opera 和iCab冒泡和捕获均不支持。

介绍事件的那篇文章中,我提了个看起来比较难以理解的问题:“假设一个元素及其祖先元素的事件句柄指向了同一事件,哪个先触发?”不出意料,这取决于浏览器。

这个问题其实很简单。假定一个父元素内有一个子元素:

-----------------------------------
| element1                        |
|   -------------------------     |
|   |element2               |     |
|   -------------------------     |
|                                 |
-----------------------------------

二者均有一个onClick事件句柄。如果用户点击了元素2,就会触发元素1和元素2的点击事件。可两个事件哪个先被触发呢?哪个事件句柄先执行呢?换句话说,事件顺序是怎样?

两种模型

可以预见的是,很久以前Netscape和Microsoftde的做法就是截然不同的。

  • Netscape指定元素1的事件先发生,称之为事件捕获

  • Microsoft表示元素2的事件先发生,称之为事件冒泡.

以上的两种做法完全背道而驰。Explorer只支持事件冒泡。Mozilla,Opera 7和Konqueror冒泡和捕获均支持。旧版本的Opera和iCab冒泡和捕获均不支持。

事件捕获

当你使用事件捕获时:

               | |
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

元素1的事件句柄先被触发,元素2的事件句柄后被触发。

事件冒泡

当你使用事件冒泡时:

               / \
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

元素2的事件句柄先被触发,元素1的事件句柄后被触发。

W3C 模型

W3C明智地在争论中保持了中立。任何发生在W3C 事件模型 中的事件首先会被捕获,直到它到达目标元素才会冒泡。

                 | |  / \
-----------------| |--| |-----------------
| element1       | |  | |                |
|   -------------| |--| |-----------     |
|   |element2    \ /  | |          |     |
|   --------------------------------     |
|        W3C event model                 |
------------------------------------------

Web开发者可以选择是否在捕获或冒泡阶段注册一个事件句柄。这可以通过在先进模型那篇有相应解释的addEventListener()方法实现。如果它的最后一个参数是true,事件句柄会为捕获阶段而设置,如果是false,事件句柄会为冒泡阶段而设置。

假设你这样做了

element1.addEventListener('click',doSomething2,true)
element2.addEventListener('click',doSomething,false)

假如用户点击元素2,会发生以下情况:

  1. 点击事件发生在捕获阶段。事件看起来好像元素2的任何祖先元素都有对应于捕获阶段的onclick事件句柄。

  2. 元素1上绑定的doSomething2()事件被执行。

  3. 事件传递到目标,没有发现任何一个对应捕获阶段的事件句柄。事件移向冒泡阶段并执行在冒泡阶段为元素2注册的doSomething()。

  4. 事件又一次向上传递并检查目标的任何祖先元素是否有对应冒泡阶段的事件句柄。最后没有发现任何句柄,因此什么也没发生。

顺序反过来就是

element1.addEventListener('click',doSomething2,false)
element2.addEventListener('click',doSomething,false)

现在如果用户点击元素2,会发生以下情况:

  1. 点击事件发生于捕获阶段。事件会查看元素2的任何祖先元素是否存在对于捕获阶段的onclick事件句柄,但没有发现。

  2. 事件传递到目标。事件移动到自己的冒泡阶段并执行为元素2注册的对应冒泡阶段的doSomething()。

  3. 事件再次向上移动并检查目标的任何祖先元素是否有对应冒泡阶段的事件句柄。

  4. 事件在元素1上发现了事件句柄。于是doSomething2()被执行。

兼容传统模型

在支持W3C DOM的浏览器中,一个传统的事件注册

element1.onclick = doSomething2;

被视为在冒泡阶段注册。

事件冒泡的使用

很少有web开发者自觉使用事件捕获或冒泡。现在的Web网页没有必要将一个冒泡事件与几个不同的事件句柄绑定。用户可能会对点击一次鼠标后发生多个动作感到困惑,而你通常会保持你的事件处理脚本彼此分离。当用户点击了一个元素,一个动作被触发,点击另一个元素就会触发另一个动作。

当然在未来这种情况也许会改变,能有向上兼容的模型当然更好。但现在事件捕获和冒泡的主要实际应用是默认功能的注册。

这总会发生

你首先需要理解事件捕获或冒泡总会发生。如果你为整个文档定义了一个普通的onclick事件句柄:

document.onclick = doSomething;
if (document.captureEvents) document.captureEvents(Event.CLICK);

文档中的任何点击事件都将冒泡到文档并触发那个事件句柄。仅当一个在它之前的事件处理脚本命令该事件停止冒泡,事件才不会冒泡到文档。

使用

由于任何事件都要在文档上结束,因此默认事件句柄成为可能。假设你有下面这个页面:

------------------------------------
| document                         |
|   ---------------  ------------  |
|   | element1    |  | element2 |  |
|   ---------------  ------------  |
|                                  |
------------------------------------

element1.onclick = doSomething;
element2.onclick = doSomething;
document.onclick = defaultFunction;

现在假如用户点击元素1或元素2,doSomething()被执行。只要愿意,你可以终止事件的传递。如果你没有终止它,事件会冒泡到defaultFunction()。如果用户点击了其他地方,defaultFunction()也被执行。这在某些时候会很有用。

在拖拽脚本中设置文档宽度事件句柄很有必要。通常一个图层的mousedown事件会选中这一图层,并使它响应mousemove事件。尽管mousedown为了避免浏览器bug通常在这一图层注册,但其他的事件句柄一定是文档宽度。

记住浏览器法则第一条:任何事都可能发生,尤其是当你没有准备时。可能当用户大幅度地移动鼠标时脚本无法正常工作,导致鼠标不会在图层上出现。

  • 如果onmousemove事件句柄注册给了图层,图层就不会对鼠标移动做出反应,这会让人困惑。

  • 如果onmouseup事件句柄在图层上被注册,事件就不会被捕获。所以图层会保持对鼠标的反应,甚至当用户以为自己放下图层后仍会保持反应。

所以在这种情况下冒泡是很有用的,因为在文档层面注册你的事件句柄能保证它们总会被执行。

关闭这个功能

但你经常想要停用所有的捕获和冒泡,因为这样函数间就不会彼此干扰。除此之外如果你的文档结构很复杂(有很多嵌套表格之类),你可以通过关闭冒泡来节省系统资源。浏览器必须查看事件目标的每一个祖先元素是否存在事件句柄。即使什么都没发现,搜索仍然会耗费不少时间。

在微软模式下你必须设置事件的cancleBubble属性的值为true.

window.event.cancelBubble = true

W3C模型中你必须调用stopPropagation()方法。

e.stopPropagation()

这会阻止冒泡阶段事件的传递。在跨浏览器时:

function doSomething(e)
{
  if (!e) var e = window.event;
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
}

在浏览器中设置cancleBubble属性无法保证不会有负面效果。浏览器会创建属性。当然它并没有真正禁止冒泡,但任这种分配本身是安全的。

当前目标

正如早先所见,拥有target或srcElement的事件包含了事件发生时对元素的一个引用。我们的例子是元素2,因为用户会点击它。

理解在冒泡和捕获阶段(或任意一个)目标不变是很重要的:它始终保持对元素2的引用。

但假设我们注册了以下这些事件句柄;

element1.onclick = doSomething;
element2.onclick = doSomething;

如果用户点击元素2,doSomething()会被执行两次。但你怎么知道是哪个HTML元素最近绑定了这个事件?target/srcElement没有给出线索,因为元素2是事件的源头,它们经常指向元素2。

为解决这个问题W3C增加了currentTarget属性。它包含了最近绑定了事件的元素的引用:这正是我们需要的。不幸的是,微软模式并没有一个与之相似的属性。

你可以使用this关键字.在例子中它指向事件绑定的那个HTML元素,就像currentTarget。

微软模型的问题

但当你使用微软事件注册模型时this关键字没有指向HTML元素。结合微软模型中一个与currentTarget类似的属性的缺点,这意味着如果你这样做的话:

element1.attachEvent('onclick',doSomething)
element2.attachEvent('onclick',doSomething)

你无法知道是哪个HTML元素最近绑定了事件。这是微软事件注册模型最严重的问题,也是我选择从不使用它的原因,哪怕是IE/WIN才有的应用我也不使用。

I hope Microsoft will soon add a currentTarget–like property — or maybe even follow the standard? Web developers need this information. 我希望微软可以尽快地添加一个类似currentTarget的属性—或者干脆遵从标准?Web开发者需要这个好消息。

尾声

如果你从头到尾看完了这篇文章,建议你应该继续看看鼠标事件

来源:原文链接,翻译来源:众成翻译


vue 监听组件原生事件_使用vue中的修饰符.native

.native主要用于监听组件根元素的原生事件,主要是给自定义的组件添加原生事件。官方对.native修饰符的解释为:有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 v-on 的修饰符 .native 。

vue自定义移动端touch事件,点击、滑动、长按事件

vue自定义指令_监听移动端touch事件,包括:点击、滑动、长按等事件,加强对Touch的一些理解。Vue.directive指令封装的使用

js 按键监听_JS实现监听组合按键

有些时候,我们需要在网页上,增加一些快捷按键,方便用户使用一些常用的操作,比如:保存,撤销,复制、粘贴等等。下面简单梳理一下思路,我们所熟悉的按键有这么集中类型:

js动态元素绑定事件_jquery、原生js实现动态添加新元素_监听事件

在实现click绑定事件的时候,发现只能对已经加载好的元素进行绑定,如果后来通过js动态插入的元素则无法绑定事件

你知道哪些html事件不能冒泡?

我们都知道一般事件的流程是:事件捕捉——>目标元素发生事件——>事件冒泡。但是不是所有的事件和click事件都一样是冒泡的,那么如何判断给事件是否不能冒泡呢?

js中的事件委托或是事件代理详解

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

js操作DOM事件中event的target和currentTarget的区别

target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象。

js事件冒泡和事件捕获详解

事件冒泡是由IE开发团队提出来的,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播。事件捕获是由Netscape Communicator团队提出来的,是先由最上一级的节点先接收事件,然后向下传播到具体的节点。

通过原生js对DOM事件的绑定的几种方式总汇

在网页开发中经常会有交互操作,比如点击一个dom元素,需要让js对该操作做出相应的响应,这就需要对Dom元素进行事件绑定来进行处理,js通常有三种常用的方法进行事件绑定:在DOM元素中直接绑定;在JavaScript代码中绑定;绑定事件监听函数。