事件高级(事件对象,事件流,事件委托)

504 阅读4分钟

const和let的区别

早些时候js 声明变量 只能使用 var

es6 (新版本的js) 出现之后,只使用 let 或者const

let 声明的变量,可以被修改

const 声明的变量 ,不可以被修改。const是常量,固定不变的

const 作用:

  • 在程序设计的时候,如果发现有一些数据,永远不会被更改,优先使用const

复杂类型的数据 补充

1.普通类型的数据 ,直接写 =, 表示修改数据,报错

const num1 = 1
    num1 = 2
    console.log(num1); 报错

2.复杂类型的数据 ,直接写 =, 表示修改,报错

3.复杂类型的数据,写 = 相对于重新开辟新空间

const arr = []
    arr.push(1) //新增一个元素。但是没有修改过数组的地址
    arr = 234 //报错  开辟了新开间
    console.log(arr); 

总结:const 不能被修改的判断的方式:看它有没有写 = 号即可,或者自增自减

能使用const就不用let 。当不知道什么时候用const,就使用 let

7d46e2215cbc196376a3bd5f79035e6.png

事件高级

一、事件对象(重点)

1.定义

也是一个对象,事件触发的那一瞬间所包含的信息,比如说鼠标坐标的信息;键盘按下信息

2.获取事件对象

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

image-20220412083600663

 <title>事件对象</title>
</head>
<body>
  <button>点击</button>
  <script>
    let button = document.querySelector('button')
    // 事件监听
    button.addEventListener('click',function (event) {
      console.log(event);
    })
  </script>

3.事件对象常见属性

1.type 输出当前的事件类型,比较少用

2.clientX, clientY 返回鼠标的位置,参照物- 是页面的左上角

image-20220411162601149

7d46e2215cbc196376a3bd5f79035e6.png 3.offsetX, offsetY 返回鼠标的位置,参照物-是被点击的元素的左上角

4.key 用户当下按下的键盘的值

小鸟跟随鼠标案例

需求:一张图片一直跟着鼠标移动

 <title>事件对象常见属性</title>
    <style>
      img {
        width: 50px;
        position: absolute;
      }
    </style>
  </head>
  <body>
    <img src="./birdie.png" alt="" />
    <script>
      // 获取img dom元素
      let img = document.querySelector("img");
      // 事件监听,添加事件对象
      img.addEventListener("mousemove", function (event) {
        let left = event.clientX
        let top = event.clientY
        img.style.left = left + 'px';
        img.style.top = top + 'px' ;
      });
    </script>
  </body>

按下回车发布微博案例

需求:按下回车键盘,可以发布信息

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>事件对象key练习</title>
    <style>
      textarea {
        width: 600px;
        height: 100px;
      }
      ul {
        list-style: none;
      }
    </style>
  </head>
  <body>
    <textarea name="" id="" cols="30" rows="10"></textarea>
    <button>发布</button>
    <ul>
      <!-- <li>苹果</li> -->
    </ul>
    <script>
      // 获取ul 元素
      let ul = document.querySelector("ul");
      // console.log(ul);
      // 获取文本域的元素
      let textarea = document.querySelector("textarea");
      // console.log(textarea);
      // 获取发布按钮
      let button = document.querySelector("button");

      // 给发布注册事件
      button.addEventListener("click", function (event) {
        // 新建li节点
        let li = document.createElement("li");
        li.innerText = textarea.value;
        ul.appendChild(li);
        textarea.value = ''
      });
      // 在文本域中按下回车键也是实现发布按钮
      textarea.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          // console.log('执行和按钮一样的功能');
      
          event.preventDefault() //组织浏览器标签的默认行为
          let li = document.createElement("li");
          li.innerText = textarea.value;
          ul.appendChild(li);
          textarea.value = ''
        }
      });
    </script>
  </body>
</html>

补充:event.preventDefault() //组织浏览器标签的默认行为

二、事件流

1.事件流和两个阶段说明

事件流:指的是事件完整执行过程中的流动路径 简单来说给多个父子结构的标签绑定事件,先点击了子元素,产生事件流动

捕获阶段 :是从父元素到子元素

冒泡阶段:是从子元素到父元素

image-20220412085621783

事件流动方向 默认是使用了冒泡

<title>事件流动捕获和冒泡</title>
    <style>
      div {
        width: 400px;
        height: 400px;
        padding: 20px;
        overflow: hidden;
      }
      .a {
        background-color: pink;
      }
      .b {
        background-color: yellow;
      }
      .c {
        background-color: purple;
      }
    </style>
  </head>
  <body>
    <div class="a">
      爷爷
      <div class="b">
        爸爸
        <div class="c">儿子</div>
      </div>
    </div>
    <script>
      let a = document.querySelector(".a");
      a.addEventListener("click", function () {
        console.log('a 爷爷');
      });
      let b = document.querySelector(".b");
      b.addEventListener("click", function () {
        console.log('b 爸爸');
      });
      let c = document.querySelector(".c");
      c.addEventListener("click", function () {
        console.log('c 儿子');
      });
    </script>

当点击儿子的时候,顺序是从儿子 ->爸爸 ->爷爷

image-20220412094235938

补充

addEventListener可以修改触发事件,让它选择使用捕获还是冒泡(默认)

addEventListener(事件类型,事件处理函数,捕获还是冒泡(默认值是false ,可以省略))

addEventListener(‘click’,function(){},true)捕获

以后是代码开发过程中,很少使用捕获阶段,还是继续使用默认的冒泡阶段

2.阻止事件冒泡

原因

因为默认就有冒泡模式的存在,所以容易导致事件影响到父元素,所以如果想要把事件限制在当前元素内,就需要阻止事件流动

语法

event.stopPropagation()

<title>阻止事件冒泡</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      div {
        width: 400px;
        height: 400px;
        padding: 20px;
        overflow: hidden;
      }
      .a {
        background-color: pink;
      }
      .b {
        background-color: yellow;
      }
      .c {
        background-color: purple;
      }
      p {
        width: 400px;
        height: 400px;
        background-color: aqua;
      }
    </style>
  </head>
  <body>
    
    <p>1</p>
    <div class="a">
      爷爷
      <div class="b">
        爸爸
        <div class="c">儿子</div>
      </div>
    </div>

    <script>
      // 需求:点击不同的div显示不同的颜色
      // 方式一:因为事件流的默认方向是冒泡阶段,比较粗暴的解决方式可以在每个div后面加上true,改变事件流动的方向
      // 方式二:在事件对象中,event找到一个方法,阻止冒泡 event.stopPropagation()
       let p = document.querySelector('p')
      // console.log(p);
      let a = document.querySelector(".a");
      a.addEventListener("click", function () {
        p.style.backgroundColor = 'pink'
        // 设置p文本内容 = 文本内容+1 需要注意转换隐式模式
        p.innerText = +p.innerText + 1
        console.log("a 爷爷");
      },true);
      let b = document.querySelector(".b");
      b.addEventListener("click", function (event) {
        p.style.backgroundColor = 'yellow'
        p.innerText = +p.innerText  + 10
        //阻止事件冒泡
        event.stopPropagation()
        console.log("b 爸爸");
      },true);
      let c = document.querySelector(".c");
      c.addEventListener("click", function (event) {
        p.style.backgroundColor = 'purple'
        p.innerText = +p.innerText + 100
         //阻止事件冒泡
        event.stopPropagation()
        console.log("c 儿子");
      });
    </script>

3.阻止标签的默认行动

3.1 a 标签的点击跳转

event.preventDefault() //阻止浏览器标签的默认行为

3.2 form表单中button点击刷新行为

  • 解决方式一:阻止默认行为 - form表单 有一个submit事件。理解提交表单的触发,-点击按钮的时候触发
  • 解决方式二 :给button按钮绑定点击事件,也去阻止试试
  • 解决方式三:给button按钮添加一个type ='button'
  • 解决方式四:换成input 标签type = 'button'
  • 解决方式五:把button移出form 表单的区域

案例代码

 <title>阻止标签的默认行为</title>
  </head>
  <body>
    <a href="http://baidu.com">百度</a><br />
    <form>
      <!--方式3 <button type="button">点击自动刷新</button> -->
      <!--方式4 <input type="button" value="点击我会刷新"> -->
    </form>
      <!-- 方式5:把button移出form 表单的区域 -->
      <button>不会再刷新</button>
  </body>
  <script>
    let a = document.querySelector("a");
    let button = document.querySelector("button");
    let form = document.querySelector('form')
    a.addEventListener("click", function (event) {
      // 阻止标签默认行为
      event.preventDefault();
      console.log("点击事件触发了");
    });
  // 方式1: form.addEventListener("submit", function (event) {
  //     event.preventDefault();
  //     console.log("点击刷新");
  //   });
    //方式2 button.addEventListener("click", function (event) {
    //   event.preventDefault();
    //   console.log("点击刷新");
    // });
    // 
  </script>

3.3鼠标右键弹出菜单

contextmenu 鼠标右键

image-20220412102842159

三、事件委托(重点)

1.定义

利用事件流的特征来解决一些开发需求的技巧

2.原理

事件委托其实就是利用了事件冒泡的特点。给父元素添加事件,子元素就可以触发

3.优点

给父元素添加事件,可以提高性能

4.实现

event.target 你当前点击的是哪个标签,(点击最深层的那个标签)

5.优化

event.target.nodeName 当前点击的元素的标签名 大写

渲染学生信息案例

需求如下

image-20220412231248642

image-20220412231306756

效果图

image-20220412231334746

思路分析图

image-20220412231500719

案例代码

<!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>学生信息综合案例</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      a {
        text-decoration: none;
        color: #721c24;
      }
      h1 {
        text-align: center;
        color: #333;
        margin: 20px 0;
      }
      table {
        margin: 0 auto;
        width: 800px;
        border-collapse: collapse;
        color: #004085;
      }
      th {
        padding: 10px;
        background: #cfe5ff;

        font-size: 20px;
        font-weight: 400;
      }
      td,
      th {
        border: 1px solid #b8daff;
      }
      td {
        padding: 10px;
        color: #666;
        text-align: center;
        font-size: 16px;
      }
      tbody tr {
        background: #fff;
      }
      tbody tr:hover {
        background: #e1ecf8;
      }
      .info {
        width: 900px;
        margin: 50px auto;
        text-align: center;
      }
      .info input {
        width: 80px;
        height: 25px;
        outline: none;
        border-radius: 5px;
        border: 1px solid #b8daff;
        padding-left: 5px;
      }
      .info button {
        width: 60px;
        height: 25px;
        background-color: #004085;
        outline: none;
        border: 0;
        color: #fff;
        cursor: pointer;
        border-radius: 5px;
      }
      .info .age {
        width: 50px;
      }
    </style>
  </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>
      /*  需求:添加数据,删除数据 */
      // 1.1设置一个数组,对象存放表格要显示的数据  方便管理
      let array = [];
      // 2.给录入 绑定点击事件
      // 获取录入
      let button = document.querySelector(".add");
      // 获取tbody
      let tbody = document.querySelector("tbody");
      // 获取姓名
      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.3调用渲染页面的函数
      renderByArr();

      // 2.给按钮注册事件
      button.addEventListener("click", function () {
        // 2.1把表单数据合并到一个新的对象中
        let newDate = {
          // 随机生成时间,时间戳
          num: +new Date(),
          uname: uname.value,
          age: age.value,
          gender: gender.value,
          salary: salary.value,
          city: city.value,
        };
        // 2.2给数组新增对象数据
        array.push(newDate);
        // 2.3数组发生改变后,重新调用渲染页面
        renderByArr();
        // console.log(array);
        // 2.4点击发布后数据清零
        uname.value = "";
        age.value = "";
        gender.value = "男";
        salary.value = "";
        city.value = "北京";
      });
      // 删除数据
      // 3.tbody绑定点击事件,同时判断被点击的是不是删除标签
      tbody.addEventListener("click", function (event) {
        // 3.1 事件触发了先判断当前点击的是不是删除
        if (event.target.nodeName === "A") {
          // date-index 自定义属性
          // 3.2  在点击删除标签的时候,知道当前点击的是第几个元素
          // <a  date-index = '2' href="javascript:" class="del">删除</a>
          // 获取到a标签上的存放的index
          const index = event.target.dataset.index;
          // 3.3数组删除元素
          array.splice(index, 1);
          // 3.4数组发生改变后,重新调用渲染页面
          renderByArr();
        }
      });

      // 1.1根据数组渲染表格
      function renderByArr() {
        // 拼接开头
        let html = ``;
        for (let index = 0; index < array.length; index++) {
          html += `<tr>
          <td>${array[index].num}</td>
          <td>${array[index].uname}</td>
          <td>${array[index].age}</td>
          <td>${array[index].gender}</td>
          <td>${array[index].salary}</td>
          <td>${array[index].city}</td>
          <td>
            <a  date-index${index} href="javascript:" class="del">删除</a>
          </td>
        </tr>`;
        }
        // 把生成的tr插入到tbody中
        tbody.innerHTML = html;
      }
    </script>
  </body>
</html>