事件监听器

事件处理器

在前面的学习中,如果我们要给元素添加一个事件,一般会通过操作 HTML 属性的方式来实现,这种方式其实也叫作“事件处理器”,如下所示。

  1. oBtn.onclick = function(){...};
  1. 虽然这种方式简单高效,但是当一个元素有如下情况时:
oBtn.onclick = function(){OPTION1};
oBtn.onclick = function(){OPTION2};
oBtn.onclick = function(){OPTION2};
JavaScript 最终只会执行最后一次 onclick。可以看出,事件处理器不能为一个元素添加多个相同事件。

对于同一个元素来说,确实很少需要添加多个相同事件。可是,有些情况下也确实需要这么做,如在点击【提交表单】按钮时,需要验证用户输入的全部数据,然后再通过 AJAX 将其提交给服务器。

如果要为一个元素添加多个相同的事件,该如何实现呢?这就需要用到另外一种添加事件的方式了,那就是——事件监听器

事件监听器

绑定事件

所谓的“事件监听器”,指的是使用 addEventListener( )方法为一个元素添加事件,我们又称之为“绑定事件”。

obj.addEventListener(type,fn,false)
obj 是一个 DOM 对象,指的是使用 getElementById( )、getElementsByTagName( )等方法获取的元素节点。

type 是一个字符串,指的是事件类型。如单击事件用 click,鼠标移入用 mouseover 等。一定要注意,这个事件类型是不需要加上“on”前缀的。

fn 是一个函数名,或是一个匿名函数。false 表示事件冒泡阶段调用。

<!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>Document</title>
  <script>
    window.onload = function(){
      var oBtn = document.getElementById("btn");
      oBtn.addEventListener("click",alertMsg,false);
      function alertMsg(){
        alert("JS")
      }
      //fn 是一个匿名函数,效果相同
      // oBtn.addEventListener("click",function(){
      //   alert("JS")
      // },false);
    }
  </script>
</head>
<body>
  <input type="button" id="btn" value="按钮">
</body>
</html>
<!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>Document</title>
  <script>
    window.onload = function(){
      var oBtn = document.getElementById("btn");
      oBtn.addEventListener("click",function(){
        alert("1")
      },false);
      oBtn.addEventListener("click",function(){
        alert("2")
      },false);
      oBtn.addEventListener("click",function(){
        alert("3")
      },false);
    }
  </script>
</head>
<body>
  <input type="button" id="btn" value="按钮">
</body>
</html>

当我们点击按钮后,浏览器会依次弹出 3 个对话框。也就是说,我们可以使用“事件监听器”这种方式来为同一个元素添加多个相同的事件,而这一点是事件处理器做不到的。

一般情况下,如果是只为元素添加一个事件,下面两种方式其实是等价的。

oBtn.addEventListener("click",function(){...},false);
obj.onclick = function(){...};
<!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>Document</title>
  <script>
    window.onload = function(){
      var oBtn1 = document.getElementById("btn1");
      oBtn1.onclick = function(){
        alert("第一次")
      }
    };
    window.onload = function(){
      var oBtn2 = document.getElementById("btn2");
      oBtn2.onclick = function(){
        alert("第二次")
      }
    };
    window.onload = function(){
      var oBtn3 = document.getElementById("btn3");
      oBtn3.onclick = function(){
        alert("第三次")
      }
    };
  </script>
</head>
<body>
  <input type="button" id="btn1" value="按钮1">
  <input type="button" id="btn2" value="按钮2">
  <input type="button" id="btn3" value="按钮3">
</body>
</html>
效果中只有第三个按钮点击后会有弹窗,这是因为 JavaScript 只执行最后一次 window.onload。为了解决这个问题,我们可以使用 addEventListener( )来实现。
<!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>Document</title>
  <script>
    window.addEventListener("load",function(){
      var oBtn1 = document.getElementById("btn1");
      oBtn1.onclick = function(){
        alert("第一次")
      }
    },false);
    window.addEventListener("load",function(){
      var oBtn2 = document.getElementById("btn2");
      oBtn2.onclick = function(){
        alert("第二次")
      }
    },false);
    window.addEventListener("load",function(){
      var oBtn3 = document.getElementById("btn3");
      oBtn3.onclick = function(){
        alert("第三次")
      }
    },false);
  </script>
</head>
<body>
  <input type="button" id="btn1" value="按钮1">
  <input type="button" id="btn2" value="按钮2">
  <input type="button" id="btn3" value="按钮3">
</body>
</html>

解绑事件

在 JavaScript 中,我们可以使用 removeEventListener( )方法为元素解绑(或解除)某个事件。解绑事件与绑定事件是功能相反的操作。

<!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>Document</title>
  <script>
    window.onload = function(){
      var oP = document.getElementById("content");
      var oBtn = document.getElementById("btn");
      oP.addEventListener("click",changeColor,false);
      oBtn.addEventListener("click",remove,false);
      function remove(){
        alert("解除绑定")
        oP.removeEventListener("click",changeColor,false);
      }
      function changeColor(){
        this.style.color = "pink";
      }
    }
  </script>
</head>
<body>
  <p id="content">xuexi</p>
  <p id="btn">解除</p>
</body>
</html>
点击【解除】之后,再点击 p 元素,发现 p 元素点击事件无效了。
oP.addEventListener("click",changeColor,false);
oP.removeEventListener("click",changeColor,false);
removeEventListener( )只可以解除“事件监听器”添加的事件,不可以解除“事件处理器”添加的事件。如果要解除“事件处理器”添加的事件,我们可以使用“obj.事件名 = null;”来实现,
<!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>Document</title>
  <script>
    window.onload = function(){
      var oP = document.getElementById("content");
      var oBtn = document.getElementById("btn");
      oP.onclick = changeColor;
      oBtn.addEventListener("click",remove,false);
      function remove(){
        oP.onclick = null;
      }
      function changeColor(){
        this.style.color = "pink";
      }
    }
  </script>
</head>
<body>
  <p id="content">xuexi</p>
  <p id="btn">btn</p>
</body>
</html>

event 对象

当一个事件发生的时候,这个事件有关的详细信息都会临时保存到一个指定的地方,这个地方就是 event 对象。每一个事件,都有一个对应的 event 对象。image.png

type

<!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>Document</title>
  <script>
    window.onload = function(){
      var oBtn = document.getElementById("btn");
      oBtn.onclick = function(e){
        alert(e.type);
      }     
    }
  </script>
</head>
<body>
  <input type="button" id="btn" value="按钮">
</body>
</html>

每次调用一个事件的时候,JavaScript 都会默认给这个事件函数加上一个隐藏的参数,这个参数就是 event 对象。一般来说,event 对象是作为事件函数的第 1 个参数传入的。

其实 e 只是一个变量名,它存储的是一个 event 对象。也就是说,e 可以换成其他名字,如 ev、event、a 等,大家可以测试一下。

keyCode

event.keyCode 返回的是一个数值,常用按键对应键码如下:image.png
如果是【Shift】键、【Ctrl】键和【Alt】键,我们不需要通过 keyCode 属性来获取,而可以通过 shiftKey、ctrlKey 和 altKey 属性来获取。

<!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>Document</title>
  <script>
    window.onload = function(){
      document.onkeydown = function(e){
        if(e.shiftKey||e.altKey||e.ctrlKey){
          alert("该键被禁用");
        }
      }
    }     
  </script>
</head>
<body>
  <p>
    一杯茶,一套题,从早到晚搞学习。
  </p>
</body>
</html>
e.keyCode 返回的是一个数字,而 e.shiftKey、e.ctrlKey、e.altKey 返回的都是布尔值(true或false)。
<!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>Document</title>
  <script>
    window.onload = function(){
      var oSpan = document.getElementsByTagName("span")[1];
      window.addEventListener("keydown",function(e){
        if(e.keyCode == 38 || e.keyCode == 87){
          oSpan.innerHTML = "up";
        }else if(e.keyCode == 39 || e.keyCode == 68){
          oSpan.innerHTML = "right";
        }else if(e.keyCode == 40 || e.keyCode == 83){
          oSpan.innerHTML = "down";
        }else if(e.keyCode == 37 || e.keyCode == 65){
          oSpan.innerHTML = "left"; 
        }else{
          oSpan.innerHTML = "";
        }},false); 
      }   
  </script>
</head>
<body>
  <div>你控制的方向是:<span style="color:aqua;"></span></div>
  <div>你控制的方向是:<span style="color:aqua;"></span></div>
</body>
</html>

this

在事件操作中,可以这样理解:哪个 DOM 对象(元素节点)调用了 this 所在的函数,那么 this 指向的就是哪个 DOM 对象。

<!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>Document</title>
  <script>
    window.onload = function(){
      var oDiv = document.getElementsByTagName("div")[0];
      var oP = document.getElementsByTagName("p")[0];
      oDiv.onclick = changeColor;
      oP.onclick = changeColor;
      function changeColor(){
        this.style.color = "blue";
      }
    }
  </script>
</head>
<body>
  <div>第一个元素</div>
  <p>第二个元素</p>
</body>
</html>
在事件函数中,如果想要使用当前的元素节点,我们应该尽量使用 this 来代替oBtn、oLi[i] 等写法。