九、事件

浏览器设计的一套处理用户行为的机制。

所谓的事件,是浏览器监听用户行为的一种机制。

  1. 当用户使用鼠标 点击 一个按钮,会触发该按钮的 点击 事件 如果此时我们想要执行代码 就可以通过JS脚本设置 点击 事件的处理函数
  2. 用户鼠标双击一个按钮,会触发该按钮的双击事件

类似的事件还有很多

9.1 事件的分类

1. 鼠标事件

  • click 点击事件(一次click包含一次mousedown和 一次mouseup)
  • dblclick 双击事件
  • mousedown 鼠标按下事件
  • mouseup 鼠标抬起事件
  • mouseover 鼠标进入事件
  • mouseenter 鼠标进入事件
  • mouseout 鼠标离开事件
  • mouseleave 鼠标离开事件
  • mousemove 鼠标移动事件

2. 键盘事件

  • keydown 键盘键被按下
  • keyup 键盘键被松开
  • keypress 输入

3. 浏览器的事件

  • load 页面中所有资源都被加载完毕的时候
  • scroll 页面的卷动

4. 焦点事件

  • focus 当一个元素获取到焦点时
  • blur 当一个元素失去焦点时

5. 移动端事件

  • touchstart 触摸开始事件 会在手指按下的时候触发
  • touchmove 触摸并移动 会在手指按下并移动的时候触发
  • touchend 触摸结束事件 会在手指离开的时候触发

6. 其它事件

  • animationstart 动画开始时触发
  • animationend 动画结束时触发
  • transitionend 过渡结束时触发

9.2 事件的组成

  • 元素(也叫作事件源)
  • 事件类型(类型,见9.1)
  • 事件处理函数(事件函数)

    9.3 事件流程

  • 事件冒泡:当点击时 事件从最精确的元素 一层一层往上触发 直到最顶层元素 这个过程叫做事件冒泡

  • 事件捕获:当点击时 事件从最顶层元素 一层一层的往下触发 直到最精确元素 这个过程叫做事件捕获

    • 最精确元素: 鼠标点到谁 谁就是最精确元素
    • 最顶层元素:
      • 高级浏览器 最顶层元素是window
      • IE中 最顶层元素是 document

        9.4 事件对象

        当事件触发的时候 会产生很多的信息 这些信息被封装成一个对象 并传递到事件处理函数中
        1. div.onclick = function(e) {
        2. // e 就是事件对象 我们可以根据它获取到许多的信息 兼容性有问题
        3. var e = e || window.event;
        4. }
        鼠标事件的重要属性
  • altKey 热键alt键

  • ctrlKey 热键ctrl键
  • shiftKey 热键shift键以上属性都是布尔值,表示是否按下,没有按下为false,按下为true
  • offsetX 事件发生时鼠标位于元素内部的left位置(从鼠标位置到左边框内)
  • offsetY 事件发生时鼠标位于元素内部的top位置(从鼠标位置到上边框内)
  • clientX 事件发生时鼠标到视口的左边距离
  • clientY 事件发生时鼠标到视口的上边距离
  • pageX 事件发生时鼠标到页面的左边距离(IE中无该属性,因为有版心的缘故,通常e.clientX就是e.pageX)
  • pageY 事件发生时鼠标到页面的上边距离(IE中无该属性,可以通过e.clientY + 页面卷动值来获取)
  • currentTarget 绑定事件的元素(IE中没有该属性)
  • target 触发事件的元素(IE中没有该属性,而是叫做 srcElement)

键盘事件的重要属性

  • key 表示当前的键对应的字符
  • keyCode 表示当前的键对应的字符的ASCII码

    9.5 阻止冒泡

  • 高级浏览器中 可以通过e.stopPropagation() 进行阻止事件的冒泡

    1. // 高级浏览器中
    2. // box1是box2的父元素
    3. var box1 = document.querySelector(".box1");
    4. var box2 = document.querySelector(".box2");
    5. box1.onclick = function() {
    6. console.log("这是BOX1");
    7. }
    8. box2.onclick = function(e) {
    9. e.stopPropagation();
    10. console.log("这是BOX2");
    11. }
  • IE浏览器中 可以通过e.cancelBubble = true 进行阻止事件的冒泡

    1. // IE浏览器中
    2. // box1是box2的父元素
    3. var box1 = document.querySelector(".box1");
    4. var box2 = document.querySelector(".box2");
    5. box1.onclick = function() {
    6. console.log("这是BOX1");
    7. }
    8. box2.onclick = function(e) {
    9. var e = e || window.event;
    10. e.cancelBubble = true;
    11. console.log("这是BOX2");
    12. }

    9.6 停止默认行为

    浏览器的一些事件中,带有一些默认行为 比如a标签的点击事件中 会带有跳转页面的行为 表单的点击事件中 带有提交的默认行为 滚轮事件中 带有改变页面卷动值的默认行为

  • 高级浏览器中 可以通过 e.preventDefault() 阻止默认行为

    1. // 获取元素
    2. var a = document.getElementsByTagName("a")[0];
    3. // 设置点击事件
    4. a.addEventListener("click", function(e) {
    5. console.log("点击了a标签1111");
    6. e.preventDefault();
    7. }, false);
  • IE浏览器中 可以通过 e.returnValue = true; 阻止默认行为

    1. // 获取元素
    2. var a = document.getElementsByTagName("a")[0];
    3. // 设置点击事件
    4. a.attachEvent("onclick", function(e) {
    5. console.log("点击了a标签1111");
    6. e.returnValue = true;
    7. });
  • DOM0级事件绑定方式中,可以通过return false进行阻止默认行为

    1. // 获取元素
    2. var a = document.getElementsByTagName("a")[0];
    3. // 设置点击事件
    4. a.onclick = function() {
    5. return false;
    6. }
    7. 9.7 事件绑定
  • DOM0级

    • 绑定
      • 元素.on事件类型 = 事件函数
      • 只能够绑定一个事件 因为它是对属性进行赋值
    • 移除
      • 元素.on事件类型 = null

        我们都知道,一个对象的属性只能够保存一个值。 如果对一个对象属性进行多次赋值,后面赋值的属性会替换掉前面的属性

  • DOM2级

    • 绑定
      • dom.addEventListener(type, handler, boolean)
        • type: 事件类型字符串 不带on
        • handler: 事件处理函数
        • boolean: 布尔值 决定绑定到捕获阶段还是冒泡阶段 默认是false false表示冒泡
    • 结论: 可以通过addEventListener方法进行多个事件函数的绑定 执行时是按照代码的书写顺序执行 因为代码的书写顺序决定了绑定顺序
    • 移除
      • document.removeEventListener(type, handler, boolean);
        • type: 事件类型字符串 不带on
        • handler: 事件处理函数 一定要保证函数地址是绑定的那个
        • boolean: 布尔值 决定移除的是捕获阶段还是冒泡阶段 默认是false false表示冒泡

          结论: 第二个参数是要移除的函数 函数是引用类型 引用类型的比较的是地址 所以一定要保证 移除的函数是当初绑定的那个函数本身

  • IE中的高级绑定方式(非DOM2级)

    • 绑定方式:
      • dom.attachEvent(type, handler);
        • type: 事件类型字符串 带on
        • handler: 事件处理函数
        • 没有第三个参数 意味着不能够绑定到捕获阶段特点: 可以对同一个元素绑定多个同类型事件的处理函数 执行起来是倒着执行 先绑定的后执行 后绑定的先执行
    • 移除方式:
      • dom.detachEvent(type, handler);
        • type: 事件类型字符串 带on
        • handler: 事件处理函数 要注意函数的地址问题

          9.8 事件委托

          事件委托也叫作事件代理。是一种设计模式。
          思想: 将原本子元素做的事情,委托给父元素去做。将事件绑定给父元素,父元素事件触发时,通过e.target判定触发事件的元素。决定执行对应代码
          1. // 1 获取元素 获取不可能被移除的父元素
          2. var tbody = document.querySelector("tbody");
          3. // 2 给tbody绑定事件
          4. tbody.onclick = function(e) {
          5. // e.target 这个属性指向触发事件的元素
          6. console.log(e.target)
          7. // 判定 点击到的是什么
          8. if (e.target.className === "del") {
          9. // 点击到的是移除按钮
          10. e.target.parentNode.remove();
          11. }
          12. }

          9.9 循环绑定事件时i的问题

          循环绑定事件
          1. var arr = [li, li, li];
          2. for (var i = 0; i < arr.length; i++) {
          3. arr[i].onclick = function() {
          4. console.log(i);
          5. }
          6. }
          点击时:image.png

          发现问题:点击时因为循环已经结束,所以输出的是3。而不是预想中的 0 1 2.

可是有时候我们需要用到这个循环变量当时的值,而不是循环结束后的值。
解决方案1: 添加自定义JS属性

  1. for (let i = 0; i < arr.length; i++) {
  2. // 1 给每一个元素 添加一个自定义的JS属性
  3. arr[i].index = i;
  4. arr[i].onclick = function() {
  5. // 再在函数中,通过this访问自定义属性
  6. console.log(this.shuaindexige)
  7. }
  8. }

结果:image.png
解决方案2: IIFE

  1. for (var i = 0; i < arr.length; i++) {
  2. // 2 IIFE解决循环问题
  3. (function(m) {
  4. arr[m].onclick = function() {
  5. console.log(m)
  6. }
  7. })(i)
  8. }

原理如下图:
image.png
解决方案3:数组的forEach方法

  1. arr.forEach(function(value, index) {
  2. value.onclick = function() {
  3. console.log(index)
  4. }
  5. })