JavaScript快速入门笔记(11):事件处理
本系列随笔是本人的学习笔记,初学阶段难免会有理解不当之处,错误之处恳请指正。转载请注明出处:https://www.cnblogs.com/itwhite/p/12254884.html。
注册事件处理程序
JavaScript 支持多种注册事件处理程序的方法:
- 设置 HTML 元素标签属性值为事件处理程序代码,例如:<button onclick="clickOk()">Ok</button>。
- 设置目标对象(比如:window 对象,某个 button 对象等)属性值为事件处理程序,例如:btn.onclick = function(){...}。
- 通过 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>
注意:通过返回 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>
要同时阻止事件传播,可以在调用了 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>
注:在 Stackoverflow 问答帖子中(比如:https://stackoverflow.com/questions/30473581/when-to-use-preventdefault-vs-return-false)一些人说上面的 return false 相当于同时调用了 preventDefault() 和 stopPropagation() 方法,但个人在 Chrome 中测试 return false 并不会阻止事件传播。
完。