3.1 在页面加载后执行任务

我们已经看到如何让jQuery响应网页的加载事件,$(document).ready()事件处理程序可以用来触发函数中的代码,但对这个过程还有待深入分析。

3.1.1 代码执行的时机选择

在第1章中,我们知道了$(document).ready()是jQuery基于页面加载执行任务的一种主要方式。但这并不是唯一的方式,原生的window.onload事件也可以实现相同的效果。虽然这两个方法具有类似的效果,但是,它们在触发操作的时间上存在着微妙的差异,这种差异只有在加载的资源多到一定程度时才会体现出来。

当文档完全下载到浏览器中时,会触发window.onload事件。这意味着页面上的全部元素对JavaScript而言都是可以操作的,这种情况对编写功能性的代码非常有利,因为无需考虑加载的次序。

另一方面,通过$(document).ready()注册的事件处理程序,则会在DOM完全就绪并可以使用时调用。虽然这也意味着所有元素对脚本而言都是可以访问的,但是,却不意味着所有关联的文件都已经下载完毕。换句话说,当HTML下载完成并解析为DOM树之后,代码就可以运行。

 加载样式与执行代码

为了保证JavaScript代码执行以前页面已经应用了样式,最好是在<head>元素中把<linkrel="stylesheet">标签和<style>标签放在<script>标签前面。

举一个例子,假设有一个表现图库的页面,这种页面中可能会包含许多大型图像,我们可以通过jQuery隐藏、显示、移动或以其他方式操纵这些图像。如果我们通过onload事件设置界面,那么用户在能够使用这个页面之前,必须要等到每一幅图像都下载完成。更糟糕的是,如果行为尚未添加给那些具有默认行为的元素(例如链接),那么用户的交互可能会导致意想不到的结果。然而,当我们使用$(document).ready()进行设置时,这个界面就会更早地准备好可用的正确行为。

 什么是加载完成

一般来说,使用$(document).ready()要优于使用onload事件处理程序,但必须要明确的一点是,因为支持文件可能还没有加载完成,所以类似图像的高度和宽度这样的属性此时则不一定会有效。如果需要访问这些属性,可能就得选择实现一个onload事件处理程序(或者是使用jQuery为load事件设置处理程序)。这两种机制能够和平共存。

3.1.2 基于一个页面执行多个脚本

通过JavaScript(而不是指直接在HTML中添加处理程序属性)注册事件处理程序的传统机制是,把一个函数指定给DOM元素的对应属性。例如,假设我们已经定义了如下函数:

  1. function doStuff() {
  2. //执行某种任务……
  3. }

那么,我们既可以在HTML标记中指定该函数:

  1. <body onload="doStuff();">

也可以在JavaScript代码中指定该函数:

  1. window.onload = doStuff;

这两种方式都会在页面加载完成后执行这个函数。但第2种方式的优点在于,它能使行为更清晰地从标记中分离出来。

 引用函数与调用函数

这里在将函数指定为处理程序时,省略了后面的圆括号,只使用了函数名。如果带着圆括号,函数会被立即调用;没有圆括号,函数名就只是函数的标识符或函数引用,可以用于在将来再调用函数。

在只有一个函数的情况下,这样做没有什么问题。但是,假设我们又定义了第二个函数:

  1. function doOtherStuff() {
  2. //执行另外一种任务……
  3. }

我们也可以将它指定为基于页面的加载来运行:

  1. window.onload = doOtherStuff;

然而,这次指定的函数会取代刚才指定的第一个函数。因为.onload属性一次只能保存对一个函数的引用,所以不能在现有的行为基础上再增加新行为。

通过$(document).ready()机制能够很好地解决这个问题。每次调用这个方法1都会向内部的行为队列中添加一个新函数,当页面加载完成后,所有函数都会被执行。而且,这些函数会按照注册它们的顺序依次执行2

1 即每次调用$(document).ready()方法。

2 通过window.onload虽然也可以注册多个函数,但却不能保证按顺序执行。

 公平地讲,jQuery并不是解决这个问题的唯一方法。我们可以编写一个 JavaScript函数,用它构造一个调用现有的onload事件处理程序的新函数,然后再调用一个传入的事件处理程序。这种方法可以避免$(document).ready()这类对抗性处理程序之间的冲突,但是却不具有我们刚才所讨论的那些优点。在现代浏览器中(包括IE 9),可以通过W3C标准的document.addEventListener()方法触发DOMContentLoaded事件。但是,jQuery则可以让我们不必考虑浏览器不一致性而完成这一任务。

3.1.3 .ready()的简写形式

前面提到的$(document).ready()结构,实际上是在基于document这个DOM元素构建而成的jQuery对象上调用了.ready()方法。$()函数为我们提供了一种简写方式。当给它传递一个函数作为参数时,jQuery会隐式调用.ready()。也就是说,对于:

  1. $(document).ready(function() {
  2. //这里是代码……
  3. });

也可以简写成:

  1. $(function() {
  2. //这里是代码……
  3. });

虽然这种语法更短一些,但作者推荐使用较长的形式,因为较长的形式能够更清楚地表明代码在做什么。

3.1.4 向.ready()回调函数中传入参数

在某些情况下,可能有必要在同一个页面中使用多个JavaScript库。由于很多库都使用$标识符(因为它简短方便),因此就需要一种方式来避免名称冲突。

为解决这个问题,jQuery提供了一个jQuery.noConflict()方法,调用该方法可以把对$标识符的控制权让渡还给其他库。使用jQuery.noConflict()方法的一般模式如下:

  1. <script src="prototype.js"></script>
  2. <script src="jquery.js"></script>
  3. <script>
  4. jQuery.noConflict();
  5. </script>
  6. <script src="myscript.js"></script>

首先,包含jQuery之外的库(这里是Prototype)。然后,包含jQuery库,取得对$的使用权。接着,调用.noConflict()方法让出$,以便将控制权交还给最先包含的库(Prototype)。这样就可以在自定义脚本中使用两个库了——但是,在需要使用jQuery方法时,必须记住要用jQuery而不是$来调用。

在这种情况下,还有一个在.ready()方法中使用$的技巧。我们传递给它的回调函数可以接收一个参数——jQuery对象本身。利用这个参数,可以重新命名jQuery$,而不必担心造成冲突,如下面的代码所示:

  1. jQuery(document).ready(function($) {
  2. //在这里,可以正常使用!
  3. });

或者,也可以使用刚刚介绍的简写语法:

  1. jQuery(function($) {
  2. //使用$的代码
  3. });