事件对象/事件委托

267 阅读4分钟

事件对象

获取

在事件绑定的回调函数的第一个参数就是事件对象

事件处理机制监听到用户操作之后,调用这个回调函数进行处理,同时给这个回调函数传入一个参数,这个参数就是事件对象

一般将事件对象命名为event,ev,e

// 关于事件对象里的键值,属性值,可以log打印输出看详细内容
元素.addEventListener('click', function (e) ) {}
// e.which 键码

注:事件类型不一样,那么它的事件对象里面的成员也不一样

常用属性

鼠标单击位置参照元素左上角的坐标值:offsetX,offsetY

鼠标单击位置参照视口左上角的坐标值:clientX,clientY

鼠标单击位置参照屏幕左上角的坐标值:screenX,screenY

鼠标单击位置参照页面左上角的坐标值:pageX,pageY

事件流

事件流指的是事件完整执行过程中的流动路径

只要绑定事件,就一定会有事件流

事件处理的默认机制

image.png

说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段

简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父

过程: 事件捕获 => 事件目标阶段 => 事件冒泡

当用户单击一个元素的时候:由document第一时间响应

1.判断当前单击的位置是否是我这个结构范围之内

2.判断我是否有子元素

如果有:子元素重复前面两步

如果没有子元素,就有这个元素进行响应处理

事件捕获概念

从DOM的根元素开始去执行对应的事件 (从外到里)

DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)

注:addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)

若传入false代表冒泡阶段触发,默认就是false

若是用 L0 事件监听,则只有冒泡阶段,没有捕获

事件冒泡概念

当一个元素触发事件后,会依次向上调用所有父级元素的同名事件(从里到外)

事件冒泡是默认存在的

阻止事件流动

阻止事件冒泡

因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素

若想把事件就限制在当前元素内,就需要阻止事件流动

阻止事件流动需要拿到事件对象

// 阻止事件冒泡
事件对象.stopPropagation()

注:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效

阻止默认行为

阻止默认行为,比如链接点击不跳转,表单域不提交的相关场景

// 阻止默认行为
事件对象.preventDefault()

事件委托

事件委托: 1.将事件绑定给已存在的父容器,让子元素进行触发 2.它的复用了事件委托的原理,当子元素触发事件之后,会将事件冒泡给父容器 3.如果想对真正触发事件的元素进行处理,可以使用e.target获取当前真正触发事件的元素 4.如果子元素的操作不一样,需要通过判断,一般我们会对子元素添加一个标识,判断当前触发事件的元素是否有这个标识

box.addEventListener('click', function(e) {
  // e.target:获取当前真正触发事件的对象,说白了,当前用户操作那个元素,e.target就是这个元素
  // 我们如何判断当前元素是我们需要的元素呢?
  // 1.我们会为元素添加标识,例如为元素添加一个自定义的类样式
  // 2.判断当前元素是否拥有指定的标识,如果有就是我们想要的元素
  if ((e.target.className = 'son')) {
    console.log('单击了son')
  } else if (e.target.classList.contains('sister')) {
    console.log('单击了sister')
  } else if (e.target.classList.contains('other')) {
    console.log('单击了other')
  }
})

使用场景:动态的渲染的元素的事件注册使用委托,动态渲染的元素的事件只能使用委托

注:元素操作一定要体现到数据,用户操作是的界面元素,但是本质上操作数据

综合案例

案例*用户信息管理
<!DOCTYPE html>
<html lang="en"><head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>Document</title>
  <link rel="stylesheet" href="css/user.css" />
</head><body>
  <h1>新增学员</h1>
  <div class="info">
    姓名:<input type="text" class="uname" /> 年龄:<input type="text" class="age" />
    性别:
    <select name="gender" id="" class="gender">
      <option value="男"></option>
      <option value="女"></option>
    </select>
    薪资:<input type="text" class="salary" /> 就业城市:<select name="city" id="" class="city">
      <option value="北京">北京</option>
      <option value="上海">上海</option>
      <option value="广州">广州</option>
      <option value="深圳">深圳</option>
      <option value="曹县">曹县</option>
    </select>
    <button class="add">录入</button>
  </div>
​
  <h1>就业榜</h1>
  <table>
    <thead>
      <tr>
        <th>学号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
        <th>薪资</th>
        <th>就业城市</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <!-- <tr>
        <td>1</td>
        <td>这是名称</td>
        <td>这是年龄</td>
        <td>这是性别</td>
        <td>这是工资</td>
        <td>这是所在城市</td>
        <td>
          <a href="javascript:" class="del">删除</a>
        </td>
      </tr> -->
    </tbody>
  </table>
  <script>
    //  获取元素
    let tbody = document.querySelector('tbody')
    let add = document.querySelector('.add') // 录入按钮
    let uname = document.querySelector('.uname') // 名字
    let age = document.querySelector('.age') // 年龄
    let gender = document.querySelector('.gender') // 性别
    let salary = document.querySelector('.salary') // 工资
    let city = document.querySelector('.city') // 城市
​
​
    //  1. 准备好数据后端的数据
    let arr = [
      {
        stuId: 1001,
        uname: '欧阳霸天',
        age: 19,
        gender: '男',
        salary: '20000',
        city: '上海'
      },
      {
        stuId: 1002,
        uname: '令狐霸天',
        age: 29,
        gender: '男',
        salary: '30000',
        city: '北京'
      },
      {
        stuId: 1003,
        uname: '诸葛霸天',
        age: 39,
        gender: '男',
        salary: '2000',
        city: '北京'
      }
    ]
​
    // 实现数据的渲染
    // 说到渲染:必须有 数据 + 静态结构的参与,实现方式就是 遍历数据,拼接结构,替换数据,填充到指定的dom结构中
​
    // 封装函数,以便后续调用
    function init() {
      // 1. 生成一个空字符串
      let htmlStr = ``
​
      // 2. 遍历数据,并拼接字符串,再替换数据
      arr.forEach(function (item, index) {
        // item 代表 arr里的对象元素
        // index 代表 索引值
        // 元素.属性 是获取对象里面的属性值
        htmlStr += `<tr>
                  <td>${index + 1}</td>
                  <td>${item.uname}</td>
                  <td>${item.age}</td>
                  <td>${item.gender}</td>
                  <td>${item.salary}</td>
                  <td>${item.city}</td>
                  <td>
                    <a href="javascript:" class="del" id="${item.stuId}">删除</a>
                  </td>
                </tr>`
      })
​
      // 3. 渲染到指定结构中
      tbody.innerHTML = htmlStr
    }
    // 调用函数
    init()
​
    // 实现数据的新增
    // 1. 准备好你需要新增的数据,添加到数据源
    // 2. 渲染
​
    // 为录入按钮绑定单击事件
    add.addEventListener('click', function () {
      // 生成一个对象
      // 根据数据源重新渲染结构
      let obj = {
        // 获取相应值
        // 数组里有内容时,获取到数组中的最后id号+1,否则id号为1001起始
        stuId: arr.length > 0 ? arr[arr.length - 1].stuId + 1 : 1001,
        uname: uname.value,
        age: age.value,
        gender: gender.value,
        salary: salary.value,
        city: city.value
      }
​
      // 追加到数组中
      arr.push(obj)
      // 重新渲染
      init()
    })
​
​
​
    // 实现数据删除-动态渲染的元素(新增的元素,未来的元素)的事件绑定需要使用事件委托
    // 事件委托:将事件绑定给父容器,让子元素来触发
​
    // 给父容器tbody绑定单击事件
    tbody.addEventListener('click', function (e) {
      // 判断,单击了类名为del才进行删除处理
      if (e.target.className == 'del') {
        // 点击对象id值存储
        let id = e.target.id
        // 遍历数据
        for (let i = 0; i < arr.length; i++) {
          // 判断 当点击对象id值与选中数组元素的id值相同时
          if (id == arr[i].stuId) {
            // 删除这个数组元素
            arr.splice(i, 1)
            // 重新渲染
            init()
          }
        }
      }
    })
​
  </script>
</body></html>