JavaScript(十二)
發佈於 2018-09-17
这一篇,我们讲讲 JavaScript 中非常重要的概念 —— 事件。 JavaScript 与 HTML 之间的交互是通过事件实现的。
最早的两大浏览器厂商(IE 及 Netscape)在如何在看待浏览器事件方面还是一致的。比如说,如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。 事件流描述的是从页面中接收事件的顺序。 有意思的是,IE 和 Netscape 开发团队居然提出了差不多是完全相反的事件流的概念。
IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
Netscape Communicator 团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。
建议使用事件冒泡,在有特殊需要时再使用事件捕获。
“DOM2 级事件”规定的事件流包括三个阶段:
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
事件就是用户或浏览器自身执行的某种动作。如 click、load 和 mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件监听器)。 事件处理程序的名字以 “on” 开头,因此 click 事件的事件处理程序就是 onclick,load 事件的事件处理程序就是 onload。为事件指定处理程序的方式有好几种。
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。这个特性的值应该是能够执行的 JavaScript 代码。 如,要在按钮被单击时执行一些 JavaScript,可以像下面这样编写代码:
<input type="button" value="Click Me" onclick="alert('Clicked')" /> |
|---|
在 HTML 中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本,如下:
<input type="button" value="Click Me" onclick="showMessage()" /> |
|---|
在 HTML 中指定事件处理程序有三个缺点。
通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。 每个元素(包括 window 和 document)都有自己的事件处理程序属性,这些属性通常全部小写,例如 onclick。将这种属性的值设置为一个函数,就可以指定事件处理程序,如下所示:
var btn = document.getElementById("myBtn"); btn.onclick = function() { alert("Clicked"); }; |
|---|
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
也可以删除通过 DOM0 级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为 null 即可:
btn.onclick = null; //删除事件处理程序 |
|---|
“DOM2 级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:
所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:
最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序,如果是 false,表示在冒泡阶段调用事件处理程序。
var btn = document.getElementById("myBtn"); btn.addEventListener("click", function() { alert(this.id); }, false); |
|---|
使用 DOM2 级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。 通过 addEventListener() 添加的事件处理程序只能使用 removeEventListener() 来移除,移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过 addEventListener() 添加的匿名函数将无法移除。 大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。
在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。
兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中,无论指定事件处理程序时使用什么方法:
var btn = document.getElementById("myBtn"); btn.onclick = function(event) { alert(event.type); //"click" }; btn.addEventListener("click", function(event) { alert(event.type); //"click" }, false); |
|---|
在通过 HTML 特性指定事件处理程序时,变量 event 中保存着 event 对象。
<input type="button" value="Click Me" onclick="alert(event.type)"/> |
|---|
Web 浏览器中可能发生的事件有很多类型。 “DOM3 级事件”规定了以下几类事件。
UI 事件指的是那些不一定与用户操作有关的事件。现有的 UI 事件如下:
JavaScript 中最常用的一个事件就是 load。当页面完全加载后(包括所有图像、JavaScript 文件、CSS 文件等外部资源),就会触发 window 上面的 load 事件。
与 load 事件对应的是 unload 事件,这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会发生 unload 事件。而利用这个事件最多的情况是清除引用,以避免内存泄漏。
焦点事件会在页面获得或失去焦点时触发。利用这些事件并与 document.hasFocus() 方法及 document.activeElement 属性配合,可以知晓用户在页面上的行踪。有以下 4 个焦点事件:
DOM3 级事件中定义了 9 个鼠标事件:
注意: 只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件; 如果 mousedown 或 mouseup 中的一个被取消,就不会触发 click 事件。类似地,只有触发两次 click 事件,才会触发一次 dblclick 事件。如果有代码阻止了连续两次触发 click 事件,那么就不会触发 dblclick 事件了。
有 3 个键盘事件:
有一个文本事件:
在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。 导致这一问题的原因是多方面的。首先,每个函数都是对象,都会占用内存,内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的 DOM 访问次数,会延迟整个页面的交互就绪时间。
对”事件处理程序过多”问题的解决方案就是事件委托。 事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click 事件会一直冒泡到 document 层次。也就是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。 最适合采用事件委托技术的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。