二、JavaScript事件之事件处理程序

108 阅读3分钟

事件一般是用户或者浏览器的某些动作,为响应事件而调用的函数一般称之为事件处理程序,事件处理程序一般是以“on”开头

1. HTML事件处理程序

HTML的事件处理程序都是作为HTML的一种属性,而这些属性必须对应一段可以执行的JS代码

// 第一种处理方式
<input type="button" value="Click Me" onclick="console.log('Clicked')"/>
// 第二种处理方式
<script>
  function showMessage() {
    console.log("Hello world!");
  }
</script>
<input type="button" value="Click Me" onclick="showMessage()"/>

通常事件也会为我们封装一个event对象。而对于this的指向,在上述代码中,this指向的就是元素本身。

需要注意的是在HTML中动态创建包装函数的这种发式,函数的作用域链被扩展了,在这个函数中document和函数本身的成员都可以被当作局部变量访问,具体是通过with方式实现。

function() {
      with(document) {
        with(this) { 
            // 属性值
        } 
    }
}

这种方式也说明事件处理程序可以更加方便访问自己的属性(this指向目标元素本身),那么在点击事件时想要获取当前value可以参考如下代码。

<!-- 输出"Click Me" -->
<input type="button" value="Click Me" onclick="console.log(value)">

如果这个input元素位于form元素中时,那么事件处理程序的作用域链还应包含form元素

function() {
   with(document) {
      with(this.form) {
        with(this) { 
            // 属性值
        }
      }
   } 
}

这样的话input可以不需要引用 form元素而直接访问form元素中其他成员

<form method="post">
    <input type="text" name="username" value="">
    <input type="button" value="Echo Username"  onclick="console.log(username.value)">
</form>

HTML指定事件处理程序会存在以下问题:

  • HTML在上,事件处理程序在下,那么用户已经点击过按钮,但程序还没被执行。
  • 函数的作用域链扩展问题在各个浏览器上表现不同
  • JS与HTML强耦合

2. DOM事件处理程序

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>菜鸟教程(runoob.com)</title>
</head>
<body>
  <input type="button" value="Click Me" id="mybtn" />
</body>
<script>
  let btn = document.getElementById("mybtn");
  // 1
  btn.onclick = function () {
    console.log(this);
  };
  // 2
  btn.addEventListener("click", () => {
    console.log(this.id);
  }, false);
  btn.addEventListener("click", () => {
    console.log("Hello world!");
  }, false);
  // 3
  let handler = function () {
    console.log(this);
  };
  btn.addEventListener("click", handler, false);
  btn.removeEventListener("click", handler, false);
</script>
</html>

通过上述代码我们可以发现,在JS中指定事件处理程序基本分为两步:1、获取元素,2、给元素事件处理程序赋值或添加监听器。

注释1中:所赋函数视为元素的方法,所以该函数的this指向元素,以这种方式添加的事件处理程序是 注册在事件冒泡阶段的。如何移除注册的事件呢?我们可以使用btn.onclick = null的方式。如果事件是在HTML中定义的,即作为HTML属性,以也可使用js中赋值为null的方式来移除事件

注释2中:是属于DOM2 Event中新增的属性,即使用监听器方式。addEventListener:添加事件监听,removeEventListener:移除事件监听。这两个方法暴露在所有DOM元素上,同时他们有三个参数:第一个参数事件名、第二参数事件处理函数、第三个参数一个布尔值。布尔值为true:表示在捕获阶段处理,false:表示在冒泡阶段处理,false为默认值。这种方式可以为元素添加多个事件处理程序,但无法移除事件处理程序

注释3中:与注释2不同点在于它可以移除所添加的事件处理程序。

无论是哪种方式的事件处理程序作用域都在元素,它们不能像HTML方式那种自动扩展作用域。大多数情况下事件处理程序都会被添加到事件冒泡阶段,主要原因是跨浏览器兼容性好。捕获阶段一般适用于拦截操作。

4. IE事件处理程序

IE的实现与DOM类似,但是方法不同,IE使用attachEevent与deleteEvent。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
  console.log("Clicked");**
});

attachEvent方法只有两个参数,第一个参数是带有on的。IE的这种方式作用域是全局的,因此this等于window IE8及早期版本只支持事件冒泡,所以这两个方法也是冒泡阶段的。如果给btn添加两个事件处理程序,执行顺序与添加顺序是反向的,先添加的后执行