一、事件流
1、什么是事件流?
事件流描述的是从页面中接受事件的顺序。
但有意思的是,微软(IE)和网景(Netscape)开发团队居然提出了两个截然相反的事件流概念,IE的事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕获流(event capturing)。
后来在W3C组织的统一之下,JS支持了冒泡流和捕获流,但是目前低版本的IE浏览器还是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),所以为了能够兼容更多的浏览器,建议大家使用冒泡流。
事件流包含的三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
接下来就是介绍这两种事件流概念
2、事件捕获
事件捕获流的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件
简单来说就是从父元素到子元素传播,从外向内
实现事件捕获的方式:
addEventListener(type,listener,useCapture); //第三个参数表示是否支持事件捕获
// 事件捕获 --- 从外向内
oDiv.addEventListener( 'click' , function () {
console.log('我是最外面的');
} , true)
oP.addEventListener( 'click' , function () {
console.log('我是中间的');
} , true)
oSpan.addEventListener( 'click' , function () {
console.log('我是最里面的');
} , true)
3、事件冒泡
IE提出的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点
简单来说就是从子元素到父元素传播,从内向外
// 默认是事件冒泡 --- 从里向外
oDiv.addEventListener( 'click' , function () {
console.log('我是最外面的');
})
oP.addEventListener( 'click' , function () {
console.log('我是中间的');
})
oSpan.addEventListener( 'click' , function () {
console.log('我是最里面的');
})
二、阻止事件冒泡
在实际的项目中,我们一般需要阻止事件冒泡
阻止事件冒泡:
要利用到事件对象:
e.stopPropagation()
e.cancelBubble = true
<body>
<div class="a">
<p>
<span></span>
</p>
</div>
<script>
var oDiv = document.querySelector('.a')
var oP = document.querySelector('p') ;
var oSpan = document.querySelector('span')
oDiv.onclick = function () {
console.log('div');
}
oP.onclick = function (e) {
e = e || event ;
// 阻止事件冒泡
// 主流浏览器的写法
e.stopPropagation()
// 低版本IE取消事件冒泡的方法
e.cancelBubble = true ;
if(e.stopPropagation) {
e.stopPropagation()
} else {
e.cancelBubble = true ;
}
console.log('p');
}
</script>
</body>
三、事件委托
栗子:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
如果页面上的事件处理函数过多则会对性能造成影响,如果需要让每个li都有点击事件,循环绑定事件 — 页面上实际会有很多的事件处理函数。并且循环绑定事件无法给将来的对象绑定事件。
解决方案:事件委托
事件委托的实现:
利用事件冒泡,子元素的事件都会触发父元素的事件
找到所有需要绑定事件的父元素,给父元素绑定事件,然后根据target来判断具体是由谁触发的
var oLis = document.querySelectorAll('li') ;
var oUl = document.querySelector('ul')
setTimeout(function () {
var fragment = document.createDocumentFragment() ; //创建文档碎片
for(var i = 11 ; i < 21 ; i++) {
var oLi = document.createElement('li') ;
oLi.innerHTML = i ;
fragment.appendChild(oLi) //将子元素都添加到文档碎片中
}
oUl.appendChild(fragment) //一次性将所有子元素添加到父元素中
},1000)
oUl.onclick = function (e) {
console.log(e.target.innerHTML); //点击触发
}
事件委托小结
概念:子元素的同类型事件委托给父元素来实现
优点: 1.减少事件处理函数优化页面性能
2.可以给将来的对象绑定事件
原理:事件冒泡 — 子元素的同类型事件都会触发父元素的同类型