未起效的stopPropagation
简单介绍下背景:我有一个通用登录弹窗模块,检测如果element设置了action-login-popup,点击后,会弹出通用的登录弹窗,但是如果没登录的话,要屏蔽element内子元素的事件。
按照原来的经验,在该元素响应事件中直接event.stopPropagation(); return false;搞起,结果发现并没有起效,what a fuck !!!
好吧,仔细看了下,event.stopPropagation()用法应该是没错,但是有一个问题,我绑定的事件哪个先执行呢?简单console.log下,果然是子元素的事件先响应了,怪不得没效果。
好吧,看来是时候研究下JS的事件的执行流程了。
这件事要追溯到N年前网景与微软IE的浏览器大战,当时浏览器技术发展很快,w3c组织也没完全成型,在JS事件捕获这件事上,两家公司产生了分歧:
- 微软的做法是,使用事件冒泡方式bubble,从目标元素冒泡到父元素
- 网景的做法是,使用事件捕获方式capture,从父元素一直捕获,直到目标元素
最终w3c定义规范,两种方式都用,先捕获到目标元素再冒泡到父元素
那如何使用bubble和capture模式呢?
首先需要说明的是,现在所有浏览器添加事件默认都是用的冒泡模式
常见的添加事件方法有三种
1. ele.onClick = func
这种基于DOM的方式,每次添加事件会替换原来事件,只能为冒泡模式
2. ele.attachEvent(‘onClick’, func)
这种仅为IE < 9的情况下支持,只支持冒泡模式,可以添加多个事件,按添加顺序执行
3. ele.addEventListener(‘click’, func, options)
这种方式为 IE >= 9、chrome、firefox大部分浏览器支持,支持冒泡模式和事件捕获模式,通过设置options修改模式,下面是个简单的例子:
HTML
1 | <div id="parent"> |
CSS
1 | #parent { |
JS
1 | var parent = document.getElementById('parent'); |
运行结果如下
执行事件的顺序还有一个关键因素是绑定的先后顺序
一些额外的小知识
IE < 9,是没有preventDefault和preventDefault的,需要设置event的returnValue和cancalBubble来,阻止默认事件和事件冒泡,polyfill如下:
1 | if (!Event.prototype.preventDefault) { |