JS 事件

JavaScript事件简介

   审视任何JavaScript代码的核心,你会发现正是事件是把所有东西融合在一些。在一个设计良好的JavaScript应用程序里,你将拥有数据源 和它的视觉的表示(在HTML DOM内部)。为了同步这两个方面,你必须监视用户的交互动作并试图相应地更新用户界面。DOM和JavaScript事件的结合是任何现代web应用程 序赖以工作的至关重要的组合。

异步事件vs.线程

  JavaScript里的事件系统是相当独特的。它完全地异步工作而根本不使用线程。这意味着你程序中的所有代码将依赖于其它的动作——比如用户的点击或者页面的加载——来触发。
   线程化的程序设计与异步的程序设计根本的不同点在于你怎样等待事情发生。在线程化的程序里,你需要不停地反复检查条件是否满足了。而在异步程序里你只须 简单地通过事件句柄注册一个回调函数,一旦事件发生,句柄就会通过执行回调函数来让你知道。我们来探索一下假如使用线程JavaScript程序将会怎么 编写和实际使用异步回调函数JavaScript又是怎么编写的。

  JavaScript线程

  按目前的情况来 看,JavaScript线程并不存在。你最多是使用setTimeout回调函数来模拟,但即使是那样,也并不理想。程序6-1中所示是一段假想的线程 化的JavaScript代码,在其中你正在等待,直到页面完成加载。如果JavaScript真是一个线程化语言的语言,你将不得不做那样的事。

  程序6-3. 模拟线程的JavaScript伪码
复制内容到剪贴板
代码:
// 注意:这段代码不能生效!
// 在页面加载之前,持续地检查
while ( ! window.loaded() ) { }
//页面现在已经加载了,于是开始做些事情
document.getElementByIdx_x("body").style.border = "1px solid #000";
   如你所见,这段代码里有一个循环,一直在检查window.loaded()是否返回true。且不说window对象根本没有loaded()这个函 数,那样的循环也决不会在JavaScript中起作用。这是因为JavaScript中的循环是阻塞式的(也就是说它们运行完成之前别的什么事都不会发 生)。假如JavaScript能够处理线程,你看到的情形将如图6-1所示。在图中,代码中的while循环持续地检查window是否已经加载。

  图6-1. 如果JavaScript能处理线程你将会看到什么

   在实际的情况里,因为while循环持续地运行并阻断了应用程序的正常流程,true值永远不可到达。结果是用户的浏览器将会停止响应并可能崩溃。由此 可知,如果有任何人声称在JavaScript里用while循环等待动作能够成功,他要么是说着玩,要么是迷糊得厉害。

  异步回调函数

   使用线程不断检查更新的替代方案是使用异步的回调,这正是JavaScript所使用的。直白地说,你告诉一个DOM元素,当指定的事件发生,你想要一 个函数被调用以处理它。这意味着你只提供一个对希望执行的代码的引用,而浏览器处理所有的细节。程序6-2展示了使用事件句柄和回调函数的一段简单的代 码。你会看到在JavaScript里把一个函数绑定到事件句柄(window.onload)上所需要的实际的代码。一但页面加载完 成,window.onload就会被调用。其它通常的事件如click,mousemove和submit的情形也是这样。

  程序6-2. JavaScript里的异步回调
复制内容到剪贴板
代码:
//注册一个页面加载完成时被调用的函数
window.onload = loaded;
//页面加载完成时被调用的函数
function loaded() {
    //页面现己加载完成了,于是干点事情
    document.getElementByIdx_x("body").style.border = "1px solid #000";
}
   将程序6-2的代码与6-1中的进行比较,你会看到显著的不同。唯一被立即执行的代码是将事件句柄(loaded函数)向事件监听器(onload属 性)的绑定。一旦页面完全加载,浏览器将调用与window.onload相关联的函数并执行它。JavaScript代码的流程如图6-2所示。图中展 示了在JavaScript中使用回调函数来等待页面加载的一个图示。因为实际上不可能等待事情的发生,你将一个回调函数(loaded)注册到页面加载 完成时会被调用的句柄(window.onload)上。

  图6-2. 使用回调函数等待页面加载的示意图

  我们的简单的事件监听器和处理程序还没有立即显现的一个问题是,取决于事件类型和元素在DOM中位置的不同,事件会变得多样化并能以不同的方式来处理。下一节我们将看到事件的两个阶段及其不同点。

事件的阶段

   JavaScript事件分为两个阶段执行:捕获(capturing)和冒泡(bubbling)。这意味着当事件从一个元素触发时(比如,用户点击 一个链接导致click事件被触发),哪些元素允许处理它、以什么顺序处理它,变得多样化了。我们来看图6-3中的一个执行顺序的例子。图中说明了当用户 点击页面中的第一个元素时,哪些事件句柄以什么顺序被触发。

  图6-3. 事件处理的两个阶段

   从这个简单的点击链接的例子里,你们可以看到事件的执行顺序。假设用户点击了一个
元素,文档的click句柄首先被触发,然后 是的句柄,然后是
的,等等,一直下行到元素;这称为捕获阶段。此阶段完成以后,它又再 次沿着树往上爬,
  • ,