事件委托:
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
- js中事件冒泡,子元素身上的事件会冒泡到父元素身上。
- 事件代理就是,本来应该加在子元素身上的事件,我们却把事件加在了其父级身上。
- 那就产生了问题:父级那么多子元素,怎么区分事件本应该是哪个子元素的?
- 答案是:event对象里记录的有“事件源”,它就是发生事件的子元素。
- 它存在兼容性问题,在老的IE下,事件源是 window.event.srcElement,其他浏览器是 event.target
为什么要用事件委托:
一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?,
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,
第一个好处是效率高,比如,不用for循环为子元素添加事件了
第二个好处是浏览器重绘与重排的减少, 减少内存占用率, 每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了
第三个好处是js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便
例子1. 页面有个ul包含着4个li,点击li,li背景变成红色
传统方法:
<ul>
<li>我是第1个小li</li>
<li>我是第2个小li</li>
<li>我是第3个小li</li>
<li>我是第4个小li</li>
<li>我是第5个小li</li>
</ul>
<script>
let lis = document.querySelectorAll('li')
//传统方法,li身上添加事件,需要用循环,找到每个li
lis.forEach(function (ele, index) {
ele.addEventListener('click', function () {
//点击每个li变背景颜色
this.style.background = 'red';
})
})
事件委托的方式:
<ul>
<li>我是第1个小li</li>
<li>我是第2个小li</li>
<li>我是第3个小li</li>
<li>我是第4个小li</li>
<li>我是第5个小li</li>
</ul>
<script>
//父获元素
let ul = document.querySelector('ul')
//为父元素添加单击事件,让子元素触发,当子元素触发时会冒泡给父元素,使用 e.target获取真正触发事件的事件源
ul.addEventListener('click', function (e) {
//但是会发现,鼠标点击ul身上而不是某个li身上时,获取的事件源是ul,那么整个ul背景将变红,这不是想要的结果,怎么办?
//加个判断。通过事件源的nodeName判断是不是li,是的话,才做出反应,不是的话,不理它。
if (e.target.nodeName === 'li') {
e.target.style.color = 'red'
}
})
</script>
例子2
js新生成的子元素也不用新为其添加事件
假如我再新添加个按钮,点击按钮,ul里就新增加个li,如果用传统的方法,for循环为li添加事件,问题就出现了,最开始有的5个li是有click事件的,但是后来动态生成的li里没有这个事件处理函数,因为循环的时候它还没生成。怎么办呢?只能在按钮点击后,生成li,然后再为生成的li再绑定事件,真是麻烦的很。而事件委托的方式就没事,当后来动态生成的li出现的时候,不用做改变,点它身上,还是变色的,因为事件是绑定在ul身上的。
<body>
<button>添加li元素</button>
<ul>
<li>我是第1个小li</li>
<li>我是第2个小li</li>
<li>我是第3个小li</li>
<li>我是第4个小li</li>
<li>我是第5个小li</li>
</ul>
<script>
//获取父元素
let ul = document.querySelector('ul')
let btn = document.querySelector('button')
btn.addEventListener('click', function () {
let newli = document.createElement('li')
newli.innerText = '新小li'
ul.appendChild(newli)
})
//为父元素添加单击事件,让子元素触发,当子元素触发时会冒泡给父元素,使用 e.target获取真正触发事件的子元素
ul.addEventListener('click', function (e) {
if (e.target.localName === 'li') {
e.target.style.background = 'red'
}
})
</script>
</body>