正确认识Javascript事件机制


Notice: Undefined index: HTTP_USER_AGENT in /home/work/wordpress/wp-content/plugins/crayon-syntax-highlighter/util/crayon_util.class.php on line 793

Notice: Undefined index: HTTP_USER_AGENT in /home/work/wordpress/wp-content/plugins/crayon-syntax-highlighter/util/crayon_util.class.php on line 793

Notice: Undefined index: HTTP_USER_AGENT in /home/work/wordpress/wp-content/plugins/crayon-syntax-highlighter/util/crayon_util.class.php on line 793

我之前没有系统读过JS方面的书,也没有写过多少JS代码,因此对事件处理的理解停留在照葫芦画瓢的阶段。

最近练手了一些小项目,发现自己对于事件的认识一直存在误解,因此特意在这里记录一下。

纠正概念

  • 事件:javascript标准提供了很多标准事件,但是jquery框架支持自定义事件;当事件发生的时候,JS通过一个对象表示这个事件。
  • 默认处理:浏览器对某些事件有内置的响应处理,比如:点击超链接会发生跳转;我们无法修改浏览器的默认处理行为,只能禁止它。
  • 事件监听:JS允许我们额外的(相对于默认处理而言)监听事件的发生,从而实现逻辑处理。
  • 事件冒泡:事件发生于产生交互的元素上,并沿着父路径向上冒泡,这样父级元素都可以监听到这个事件。
  • 事件捕获:与冒泡的路径相反,也可以理解为事件的下沉,事件从body向交互元素移动,这样路径上的元素都可以捕获到这个事件。

事件流程

当与某个元素发生交互后,属于这个元素的事件对象被创建出来。

接下来进入了事件捕获阶段,也就是将这个事件对象从body层开始向交互元素逐级下沉,路径上的元素如果之前注册过事件监听函数(注册事件监听的时候指定了捕获阶段回调)就会被回调。

事件抵达交互元素后发生”反弹”,也就是开始从交互元素向body冒泡,路径上的元素如果之前注册过事件监听函数(注册事件监听的时候指定了冒泡阶段回调)就会被回调。

无论是捕获还是冒泡阶段,我们都可以禁止事件的默认处理,一定要注意:

  • 默认处理只会在产生事件的那个交互元素上执行,与它冒泡或者捕获阶段经过路径上的其他元素无关。
  • 无论你在路径上的哪个元素的事件回调中禁止了事件的默认处理,最终阻止的都是交互元素的默认处理,而不是经过的那个元素。

一旦你在某个时机禁止了事件的默认处理,当冒泡结束之后浏览器都会对事件对象进行判定,如果未曾禁止过事件的默认处理,那么交互元素的默认处理将被执行,否则将不会执行。

事件的概貌到这里就说完了,最后要补充一点:冒泡流程是可以打断的,也就是立即终止向上冒泡,立即进入最终默认处理是否执行的判定。

看个例子

比较典型又容易误解事件的例子就是<a>标签了,借助这个例子我们可以看清事件”从哪来,到哪去,做什么,怎么做”。

让我们按照之前的理论分析一下这个结构,

误区1:点击<a>区域发生跳转,所以认为事件发生在<a>身上。

纠正:很多人都这样误解了js事件,其实你交互的是某个<div>,事件产生于<div>,冒泡到<a>。


误区2:认为事件从<div>冒泡到<a>后,执行了<a>的默认处理,所以发生了跳转。

纠正:事件的默认处理只发生在交互元素自身,下沉和冒泡经过的父级元素没有默认处理这一说。那么为什么<div>的默认处理是跳转链接呢?因为浏览器在渲染DOM的时候,会给<a>标签的所有孩子的click事件添加内置的默认处理,也就是跳转链接。同理,对于一个没有处在<a>标签内部的div,显然浏览器也不会给它附加这种默认处理行为。


误区3:认为只有在<div>上监听click事件,并调用event.preventDefault()才能”跳转链接”的默认处理。

纠正:回顾事件流程可知,在捕获/冒泡的任意元素上监听得到事件,都可以调用event的preventDefault禁止掉默认行为,当冒泡结束后浏览器会判定是否需要为交互元素执行默认处理,仅此而已。因此,在上例中我们可以在<div>的任意父级元素上监听事件(无论是捕获还是冒泡),然后禁止掉最终为交互元素执行的默认处理(也就是链接跳转)。


下面,我们试验一下是不是这样:

click event来自于被点击的<div>,其默认行为是click后链接跳转,这个默认行为是因为<div>是<a>的孩子而被浏览器附加的默认处理。

因此,我们在冒泡路径上的<a>元素,<body>元素都可以禁掉默认行为的最终执行:

本文内容比较基础,但是也很容易忽视,希望对你有帮助。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~