事件订阅
浏览器的交互师通过浏览器的内置事件响应实现的,常见的浏览器事件有文档载入(load),元素点击(click)等,订阅浏览器事件可以通过如下方式实现。
一、HTML属性的事件句柄(DOM0)
<!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body onload="alert('loaded')"> </body> </html>
通过HTML标签的属性直接订阅执行代码。当事件触发时,属性值内的可执行Javascript代码将会被执行,如果遇到未处理异常,代码终止,余下的代码将不会被执行。
二、DOM属性的事件句柄
<!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>Document</title> <script> window.onload = function (){alert("loaded")}; </script> </head> <body> </body> </html>
我们需要给回调参数编写一个function的回调,这种方法比直接在HTML中输入执行代码会优雅一些,但是这种方式也有一个缺点,就是它只能处理一个回调请求。对例子里的window.onload进行多次赋值,将会覆盖原型的回调函数,最终执行的回调函数将会是最后一次赋的值。
<!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>Document</title> <script> window.onload = function () {alert("loaded")}; window.onload = function () {alert("loaded (another handler)")}; </script> </head> <body> </body> </html>
如上代码将会弹出“loaded (another handler)” 内容。基于这种特性,我们有了这种方式订阅事件的解绑方案,那就是将对应DOM对象的事件回调属性赋值为null。
三、事件句柄注册(DOM2)
DOM2指定了更为灵活的事件订阅机制,window的两个方法addEventListener和removeEventListener。
// type:事件类型 // listen:回调函数 // useCapture:在事件传递的哪个阶段触发 window.addEventListener(type, listen, useCapture); window.removeEventListener(type, listen, useCapture);
使用addEventListener方法可以为DOM元素的某个事件指定多个回调函数,并且可以单独取消事件订阅。
<!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>Document</title> <script> window.addEventListener("load", function(){alert("loaded")}, false); </script> </head> <body> </body> </html>
但是IE8以及以下版本的IE浏览器并不支持这两个方法,它提供了另外一组方法来支持多回调订阅,attachEvent与detachEvent。
// type:事件类型 // listen:回调函数 window.attachEvent(type, listen); window.detachEvent(type, listen);
并且,IE的type参数与W3C设计的接口参数有些区别:
1、IE的type参数比DOM2设定的addEventListener方法的type参数前面多了一个on。
2、它并不支持指定事件的触发阶段的定制,即他不支持useCapture参数。
3、它所订阅的函数在全局域中执行,所以listen内的this指向的是全局对象window。
<!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>Document</title> <script> window.attachEvent("onload", function(){alert("loaded")}); </script> </head> <body> </body> </html>
事件传播
浏览器事件传播分为三个阶段,分别是向下捕获、到达目标元素和向上冒泡。一个事件被触发后,完整的事件传播将会经历这三个阶段,并依次通知传播链里的所有文档元素调用自己的订阅处理函数来处理事件。
addEventListener(type, listen, useCapture)的最后一个参数指示了在哪个阶段调用事件回调。如果是true,那么在type事件的捕获阶段将会调用listen方法,如果是false,那么就在冒泡阶段触发。
下面是一个说明传播阶段的实例代码。该代码必须在IE9以及以上IE浏览器/FireFox/Chrome浏览器里执行。在线预览
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>RunJS</title> <style> #base{ margin:0 auto; padding:50px; width:200px; height:200px; background:red; } #child{ width:150px; height:150px; background:blue; } </style> </head> <body> <div id="base"> <div id="child"> 点击 </div> </div> <script> function S(id){ return document.getElementById(id); } S("base").addEventListener("click", function(){alert("父节点捕获阶段")}, true); S("child").addEventListener("click", function(){alert("目标捕获阶段")}, true); S("child").addEventListener("click", function(){alert("目标冒泡阶段")}, false); S("base").addEventListener("click", function(){alert("父节点冒泡阶段")}, false); </script> </body> </html>