JS的事件委托

引言:遇到下面应用场景,你要怎么办?

场景一:

我们要给100个按钮添加点击事件,怎么办?
最笨的办法:直接给100个按钮都addEventListener
但是有了事件委托后:监听这100个按钮的祖先,等冒泡的时候,判断target是不是这100个按钮中的一个 就可以了

场景二:

我们要监听目前不存在的元素的点击事件,咋办?
有了事件委托:监听祖先,等到冒泡时,判断点击的元素是不是我想要监听的元素就可以了。

事件委托这么牛,那他到底是什么,怎么实现的呢?别急,往下看。。。

一、什么是事件委托?有什么好处?

事件委托是指利用冒泡原理,把事件加到父级上,触发执行效果。

由于冒泡阶段,浏览器从用户点击的内容从下往上遍历至 window,逐个触发事件处理函数,

因此 可以监听一个祖先节点(例如爸爸节点、爷爷节点)来同时处理多个子节点的事件。

好处

  • 减少事件数量,提高性能。
  • 预测未来元素,新添加的元素仍然可以触发该事件。

---听起来不错,拉出来看看?

---好的,展示!

代码一 :“实现10(或者更多)个按钮,绑定click事件,按下后能够返回按的是第几个”
<!DOCTYPE html>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
  <div id="zxm">
    <button data-id='1'>click1</button>
    <button data-id='2'>click2</button>
    <button data-id='3'>click3</button>
    <button data-id='4'>click4</button>
    <button data-id='5'>click5</button>
    <button data-id='6'>click6</button>
    <button data-id='7'>click7</button>
    <button data-id='8'>click8</button>
    <button data-id='9'>click9</button>
    <button data-id='10'>click10</button>
  </div>
</body>
</html>

JS代码:

zxm.addEventListener('click',(e) =>{
  const t = e.target
  if(t.tagName.toLowerCase()==='button'){
    console.log('data-id 是'+ t.dataset.id)

输出结果:

代码二 :监听所有的li标签,如果用户点击li标签,就console.log('用户点击了Li标签')
<ul id="test">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>

JS代码:

// 监听父元素 ul#test
test.addEventListener('click', (e)=> {
  //通过浏览器传进来的e参数,找到当前点击元素
  const t = e.target 
  // 判断当前元素是不是Li标签
  if(t.matches('li') {
    console.log('用户点击了li')
实现思路就是:
首先监听父元素,
然后根据浏览器传进去的事件信息,拿到当前点击元素,
再判断当前点击元素是不是li元素, 如果是,就console.log('用户点击Li标签')

JS 实现封装

on('click', '#test', 'li', ()=>{
    console.log('用户点击了li')
function on(eventType, element, selector, fn) {
     // 先判断是不是element, 
    //如果传进来的是选择器,不是element本身,就先变成element,
   // 因为只有element才能监听事件
    if (!(element instanceof Element)) {
        element = document.querySelector(element)
    element.addEventListener(eventType, (e)=>{
        let target = e.target
        if (target.matches(selector)) {
            fn(e)

完整展示

但是以上这种实现有一个小问题,那就是如果被点击元素有多个父元素怎么办?

<ul id="test">
        <span>1</span>
        <span>2</span>
        <span>3</span>
        <span>4</span>
  </ul>

JS代码

递归地向上多找几层父节点,直到找到li标签,
同时还必须限定,寻找的范围不能超过 element,
拿上面的例子来说,不可以越过ul标签,去找body标签
on('click', '#test', 'li', ()=>{
    console.log('用户点击了li')
function on(eventType, element, selector, fn) {
    if (!(element instanceof Element)) {
        element = document.querySelector(element)
    element.addEventListener(eventType, (e)=>{
        let el = e.target
        // 如果匹配到了selector就跳出循环
        while(!el.matches(selector)){
            if (el === element){
                //已经找到了父元素,说明还没找到,就设置为null
                el = null
                break
            el = el.parentNode
      	// 找到了el, 就调用函数