JavaScript快速入门笔记(11):事件处理

本系列随笔是本人的学习笔记,初学阶段难免会有理解不当之处,错误之处恳请指正。转载请注明出处:https://www.cnblogs.com/itwhite/p/12254884.html

注册事件处理程序

JavaScript 支持多种注册事件处理程序的方法:

  1. 设置 HTML 元素标签属性值为事件处理程序代码,例如:<button onclick="clickOk()">Ok</button>。
  2. 设置目标对象(比如:window 对象,某个 button 对象等)属性值为事件处理程序,例如:btn.onclick = function(){...}。
  3. 通过 addEventListener() 方法为某个目标对象添加事件处理程序,例如:btn.addEventListener("click", function(){...}, false)。其中:
    • 第一个参数为事件类型(字符串)
    • 第二个参数为函数对象,表明事件发生时应该调用的对象
    • 第三个参数通常使用 false 即可(即事件使用冒泡法传播,与之相对的是“捕获法”,后续会有详细描述)

完整示例:

<p id="number">0</p>
<button onclick="add(event)">Add</button>    <!-- 1. 设置 HTML 元素标签属性值为事件处理程序代码 -->
<button id="dec">Dec</button>
<button id="reset">Reset</button>
<script>
    var n = 0;
    var p = document.getElementById("number");
    var d = document.getElementById("dec");
    var r = document.getElementById("reset");
    d.onclick = dec;                           // 2. 设置目标对象属性值为事件处理程序,调用 dec() 时会自动传入事件对象
    r.addEventListener("click", reset, false); // 3. 通过 addEventListener() 方法添加事件处理程序,调用 reset() 时也会自动传入事件参数
    function add(event) {   // 这个函数中的 this 指向 window 对象,因为使用的是函数调用方式
        n++;   p.innerHTML = n;
    }
    function dec(event) {   // 这个函数中的 this 指向目标对象(即第二个<button>)
        n--;   p.innerHTML = n;
    }
    function reset(event) { // 这个函数中的 this 也指向目标对象(即第三个<button>)
        n = 0; p.innerHTML = n;
    }
</script>

事件传播

事件传播是在父子(元素)对象之间进行的,比如:一个表单中有多个输入框,我们可以为每个输入框设置 change 事件处理程序,当然我们也可能会希望仅为表单自身设置 change 事件处理程序(来处理所有输入框 change 事件),这就需要输入框发生 change 事件时通知表单,通知表单的这个过程就叫“事件传播”。

由于历史原因,Microsoft 和 Netscape 的浏览器在处理事件传播时使用两种不同的传递顺序(前者自顶向下,传递过程称为事件捕获;后者自底向上,传递过程称为冒泡法传递) ,大致流程如下图所示:

为了折中这两种事件传播方式,主流的浏览器将事件传播分为两个阶段:

  • 第一阶段:按照事件捕获的顺序调用注册的捕获函数(addEventListener() 第三个参数设置为 true)
  • 第二阶段:按照冒泡法的顺序调用注册的处理函数(addEventListener() 第三个参数设置为 false)

示例:

<!DOCTYPE html>
<html>
  <body>
    <button id="test">Test</button>
    <script>
      var btn = document.getElementById("test");
      var html = document.documentElement;
      var body = document.body;
      window.addEventListener("click", function(){console.log("Captured by window")}, true);
      window.addEventListener("click", function(){console.log("Bubble up to window")}, false);
      document.addEventListener("click", function(){console.log("Captured by document")}, true);
      document.addEventListener("click", function(){console.log("Bubble up to document")}, false);
      html.addEventListener("click", function(){console.log("Captured by <html>")}, true);
      html.addEventListener("click", function(){console.log("Bubble up to <html>")}, false);
      body.addEventListener("click", function(){console.log("Captured by <body>")}, true);
      body.addEventListener("click", function(){console.log("Bubble up to <body>")}, false);
      btn.addEventListener("click", function(){console.log("Captured by <button>")}, true);
      btn.addEventListener("click", function(){console.log("Bubble up to <button>")}, false);
    </script>
  </body>
</html>
<!-- 上述程序在控制台中输出以下内容:
    Captured by window
    Captured by document
    Captured by <html>
    Captured by <body>
    Captured by <button>    <=== 《JavaScript权威指南(第六版)》17.3.6节中说它不会被调用,Chrome中测试会被调用
    Bubble up to <button>
    Bubble up to <body>
    Bubble up to <html>
    Bubble up to document
    Bubble up to window
-->

 取消浏览器默认操作和阻止事件传播

上面第一节“注册事件处理程序”中介绍了三种方法,其中 方法1 和 方法2 可以通过返回 false 来取消浏览器默认操作,例如:在表单元素的 submit 事件处理程序中返回 false,就会阻止表单提交。示例:

<!-- 示例一:通过 HTML 属性值返回 false 取消浏览器默认行为 -->
<form onsubmit="alert('Stop submit'); return false">
    Name:     <input type="input" name="user" value="Jack" /><br />
    Password: <input type="password" name="password" value="123456" /><br />
    <button type="submit">Login</button>
</form>

<!-- 示例二:通过在事件处理函数(仅用于通过对象属性设置的)中返回 false 取消浏览器默认行为 -->
<form>
    Name:     <input type="input" name="user" value="Jack" /><br />
    Password: <input type="password" name="password" value="123456" /><br />
    <button type="submit">Login</button>
</form>
<script>
    document.forms[0].onsubmit = function() {
        alert('Stop submit');
        return false;
    };
</script>
View Code

注意:通过返回 false 阻止浏览器默认操作的行为,仅限于通过 方法1 和 方法2 (即通过 HTML 元素或对象 属性注册的事件处理函数)。

通过 方法3 (即调用 addEventListener())注册的事件处理函数,可以通过事件对象的 preventDefault() 方法来阻止浏览器的默认操作(但它不会阻止事件传播),示例如下:

<!-- 示例三:通过 addEventListener() 注册的事件处理函数,调用事件对象的 preventDefault() -->
<form>
    Name:     <input type="input" name="user" value="Jack" /><br />
    Password: <input type="password" name="password" value="123456" /><br />
    <button type="submit">Login</button>
</form>
<script>
    document.forms[0].addEventListener("submit", function(e) {
        alert('Stop submit');
        e.preventDefault(); // 这里能阻止浏览器默认行为(即“提交”行为),但是不会阻止事件传播
    }, false);
    document.addEventListener("submit", function(e) {
        alert('The document object received the submit event'); // 这里会被调用,说明没有阻止事件传播
    }, false);
</script>
View Code

要同时阻止事件传播,可以在调用了 preventDefault() 方法的基础上,同时调用事件对象的 stopPropagation() 方法,例如:

<!-- 示例四:阻止浏览器默认行为,同时阻止事件传播 -->
<form>
    Name:     <input type="input" name="user" value="Jack" /><br />
    Password: <input type="password" name="password" value="123456" /><br />
    <button type="submit">Login</button>
</form>
<script>
    document.forms[0].addEventListener("submit", function(e) {
        alert('Stop submit');
        e.preventDefault();  // 这里能阻止浏览器默认行为(即“提交”行为)
        e.stopPropagation(); // 阻止事件传播
    }, false);
    document.addEventListener("submit", function(e) {
        alert('The document object received the submit event'); // 这里不会被调用,说明阻止了事件传播
    }, false);
</script>
View Code

注:在 Stackoverflow 问答帖子中(比如:https://stackoverflow.com/questions/30473581/when-to-use-preventdefault-vs-return-false)一些人说上面的 return false 相当于同时调用了 preventDefault() 和 stopPropagation() 方法,但个人在 Chrome 中测试 return false 并不会阻止事件传播。

完。

原文地址:https://www.cnblogs.com/itwhite/p/12254884.html