- 说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!
- 作者:不渴望力量的哈士奇(哈哥),十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司担任研发部门CTO。
- 荣誉:2022年度博客之星Top4、2023年度超级个体得主、谷歌与亚马逊开发者大会特约speaker、全栈领域优质创作者。
- 白宝书系列
- 启示录 - 攻城狮的自我修养
- Python全栈白宝书
- ChatGPT实践指南白宝书
- 产品思维训练白宝书
- 全域运营实战白宝书
- 大前端全栈架构白宝书
事件委托可以帮助解决添加事件监听的两个问题:
下面举两个案例来分别介绍着两个问题。
**案例一,批量添加事件监听会遇到性能问题:**页面上有一个无序列表,它内部共有20个元素,请批量给它们添加点击事件监听,实现效果:点击哪个
元素,哪个
元素就变红。
题目分析:
可以使用getElementsByTagName可以获取到li的数组,然后通过遍历的方式循环添加事件监听
<body>
<div>
<ul id="list">
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
<li>项li>
ul>
div>
<script>
var oList = document.getElementById('list');
var lis = oList.getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
//在这个函数中,this表示点击的这个元素,this涉及函数上下文的相关知识,我们在“面向对象”中再介绍
this.style.color = 'red';
};
};
script>
body>
上面案例中,每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大。并且每个事件处理函数都是不同的函数,这些函数本身也会占用内存。
**案例二,页面元素是动态添加的,每个元素都需要添加事件监听:**页面上有一个无序列表,它内部没有元素,请制作一个按钮,点击这个按钮就能添加一个
元素。并且要求每个增加的
元素也要有事件监听,实现效果点击哪个
元素,哪个
元素就变红
题目分析:
li的数量不是固定的,没办法使用循环遍历的方式去批量添加事件监听。我们给新创建的li节点添加事件监听
<body>
<button id="btn">点击我添加列表项button>
<div>
<ul id="list">ul>
div>
<script>
var oBtn = document.getElementById('btn');
var oList = document.getElementById('list');
oBtn.onclick = function () {
//创建一个孤儿节点
var oLi = document.createElement('li');
oLi.innerHTML = '我是列表项';
//上树
oList.appendChild(oLi);
//添加事件监听
oLi.onclick = function () {
oLi.style.color = 'red';
};
};
script>
body>
上面案例中,同样会出现性能问题,因为每个事件处理函数都会占用新的内存,添加几个li,就会创建几个事件监听处理函数。
为了解决上面的问题,DOM提供了一种方法,即事件委托
。
**事件委托:**利用事件冒泡机制,将
后代元素
事件委托给祖先元素
比如下图中,点击任何一个元素,事件都会通过事件冒泡传给祖先元素,我们只需要给
添加事件监听就可以了,而不需要给每一个都添加一遍事件监听。
事件委托需要结合
e.target
属性来实现
属性 | 属性描述 |
---|---|
target | 触发此事件的最早元素,即“事件源元素” |
currentTarget | 事件处理程序附加到的元素,与this非常相似 |
示例代码:
<body>
<ul id="list">
<li>我是列表项li>
<li>我是列表项li>
<li>我是列表项li>
<li>我是列表项li>
<li>我是列表项li>
ul>
<script>
var oList = document.getElementById('list');
//给ul添加事件监听
oList.onclick = function (e) {
//e.target就表示点击的那个元素,即事件源元素,将事件源元素变红
e.target.style.color = 'red';
}
script>
body>
可以自行尝试一下动态创建元素,点击按钮创建新的li,然后利用事件委托实现鼠标点击li时,文字变红。
事件委托的使用场景
当有大量元素需要批量添加事件监听时,使用事件委托可以减少内存开销
当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听
事件委托的注意事项
注意事件是否冒泡,不能委托不冒泡的事件给祖先元素
在鼠标事件监听中,onmouseenter和onmouseover都表示“鼠标进入”,它们有什么区别呢?答:onmouseenter不冒泡,onmouseover冒泡;onmouseenter这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了哪个DOM节点,就是哪个DOM节点自己触发的事件,没有冒泡的过程。
示例代码:
//因为onmouseeter不冒泡,所以鼠标进入ul中的某个li时,整个ul都会变红,因为这个事件监听针对的是ul节点元素。
oList.onmouseenter = function (e) {
e.target.style.color = 'red';
};
解决办法:将onmouseenter换成onmouseover
最内层元素尽可能不能再有额外的内层元素了,否则可能实现不了理想的效果
比如下面的例子中,li内部还包了span标签,当鼠标点击span标签的内容时,只有span标签的变红了,而不能使整个li变红。
<body>
<ul id="list">
<li><span>姓名:span>我是列表项li>
<li><span>电话:span>我是列表项li>
<li><span>地址:span>我是列表项li>
<li><span>邮箱:span>我是列表项li>
<li><span>手机:span>我是列表项li>
ul>
<script>
var oList = document.getElementById('list');
//给ul添加事件监听
oList.onclick = function (e) {
e.target.style.color = 'red';
};
script>
body>