浏览器原理面试题 前端「前端面试进阶之浏览器原理」(16)


IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段 。事件处理阶段会首先执行目标元素绑定的监听事件 。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行 。这种模型通过attachEvent 来添加监听函数 , 可以添加多个监听函数,会按顺序依次执行 。
DOM2 级事件模型,在该事件模型中,一次事件共有三个过程 , 第一个过程是事件捕获阶段 。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行 。后面两个阶段和 IE 事件模型的两个阶段相同 。这种事件模型,事件绑定的函数是addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行 。
2. 如何阻止事件冒泡
普通浏览器使用:event.stopPropagation()
IE浏览器使用:event.cancelBubble = true;
3. 对事件委托的理解
(1)事件委托的概念
事件委托本质上是利用了浏览器事件冒泡的机制 。因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件委托(事件代理) 。
使用事件委托可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗 。并且使用事件代理还可以实现事件的动态绑定,比如说新增了一个子节点 , 并不需要单独地为它添加一个监听事件 , 它绑定的事件会交给父元素中的监听函数来处理 。
(2)事件委托的特点
减少内存消耗
如果有一个列表,列表之中有大量的列表项,需要在点击列表项的时候响应一个事件:
item 1
item 2
item 3
......
item n
如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能 。因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件时再去匹配判断目标元素,所以事件委托可以减少大量的内存消耗,节约效率 。
动态绑定事件
给上述的例子中每个列表项都绑定事件 , 在很多时候,需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的,所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的 。
// 来实现把 #list 下的 li 元素的事件代理委托到它的父层元素也就是 #list 上:
// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
在上述代码中 ,  target 元素则是在 #list 元素之下具体被点击的元素,然后通过判断 target 的一些属性(比如:nodeName,id 等等)可以更精确地匹配到某一类 #list li 元素之上;
(3)局限性
当然,事件委托也是有局限的 。比如 focus、blur 之类的事件没有事件冒泡机制,所以无法实现事件委托;mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位 , 对性能消耗高 , 因此也是不适合于事件委托的 。