什么是事件
JavaScript和HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。可以使用监听器(或事件处理程序)来预定事件,以便事件发生时执行相应的代码。通俗的说,这种模型其实就是一个观察者模式。(事件是对象主题,而这一个个的监听器就是一个个观察者)
什么是事件流
事件流描述的就是从页面中接收事件的顺序。而早期的IE和Netscape提出了完全相反的事件流概念,IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。
事件冒泡、事件捕获
IE提出的事件流是事件冒泡,即从下至上,从目标触发的元素逐级向上传播,直到window对象。
而Netscape的事件流就是事件捕获,即从document逐级向下传播到目标元素。由于IE低版本浏览器不支持,所以很少使用事件捕获。
后来ECMAScript在DOM2中对事件流进行了进一步规范,基本上就是上述二者的结合。
DOM2级事件规定的事件流包括三个阶段: (1)事件捕获阶段 (2)处于目标阶段 (3)事件冒泡阶段
事件冒泡和事件捕获的区别:
(1)、事件冒泡: 从触发事件的那个节点一直到document,是自下而上的去触发事件。
(2)、事件捕获: 从document到触发事件的那个节点,即自上而下的去触发事件。
事件冒泡
<div class="classv">
我是老大
<div class="actva">我是老二
<div class="foo">我是老幺 ---------> 点击</div>
</div>
</div>
<script type="text/javascript">
// true 代表事件捕获 ,false代表事件冒泡 不写默认的是false
document.querySelector('.classv').addEventListener('click', function() {
console.log('我是老大')
})
document.querySelector('.actva').addEventListener('click', function() {
console.log('我是老二')
})
document.querySelector('.foo').addEventListener('click', function() {
console.log('我是老幺')
})
</script>
事件捕获
<div class="classv">
我是老大
<div class="actva">我是老二
<div class="foo">我是老幺 ---------> 点击</div>
</div>
</div>
<script type="text/javascript">
// true 代表事件捕获 ,false代表事件冒泡
document.querySelector('.classv').addEventListener('click', function() {
console.log('我是老大')
}, true)
document.querySelector('.actva').addEventListener('click', function() {
console.log('我是老二')
}, true)
document.querySelector('.foo').addEventListener('click', function() {
console.log('我是老幺')
}, true)
</script>
事件委托
如果有多个DOM节点需要监听事件的情况下,给每个DOM绑定监听函数,会极大的影响页面的性能,因为我们通过事件委托来进行优化,事件委托利用的就是冒泡的原理。
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var li_list = document.getElementsByTagName('li')
for(let index = 0;index<li_list.length;index++){
li_list[index].addEventListener('click', function(ev){
console.log(ev.currentTarget.innerHTML)
})
}
</script>
正常情况我们给每一个li都会绑定一个事件,但是如果这时候li是动态渲染的,数据又特别大的时候,每次渲染后(有新增的情况)我们还需要重新来绑定,又繁琐又耗性能;这时候我们可以将绑定事件委托到li的父级元素,即ul。
var ul_dom = document.getElementsByTagName('ul')
ul_dom[0].addEventListener('click', function(ev){
console.log(ev.target.innerHTML)
})
上面代码中我们使用了两种获取目标元素的方式,target和currentTarget,那么他们有什么区别呢:
- target返回触发事件的元素,不一定是绑定事件的元素
- currentTarget返回的是绑定事件的元素
因此我们总结一下事件委托的优点:
提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。