原文:Beginning jQuery
协议:CC BY-NC-SA 4.0
一、你需要知道的 JavaScript
jQuery 是一个建立在 JavaScript 之上的框架,它本身并不是一种语言。几乎没有任何 JavaScript 知识也可以编写 jQuery,但是我们不建议这样做。如果您希望能够自信地为您的站点编写 jQuery 插件,或者修改其他人编写的插件,您需要熟悉基本的 JavaScript。这就是为什么这本书从你需要知道的 JavaScript 开始。本章涵盖
- 网页上的 JavaScript 脚本
- JavaScript 中的变量和对象
- JavaScript 函数
- 条件式
- 在数组和对象上循环
- 调试 JavaScript
如果你熟悉 JavaScript,你可能想跳过这一章。这很好,但请考虑先略读一下,以确保你对所涉及的一切都感到满意。抵制跳到 jQuery 部分的诱惑——因为你会纠结于它。相信我们,在接下来的几章时间里,这些准备工作看起来都是值得的。我们在网上帮助过的许多开发人员都迫不及待地一头扎进了 jQuery,但很快就因为缺乏对 jQuery 语言的理解而停滞不前。当你写 jQuery 时,你写的是 JavaScript,但是使用的是 jQuery 库。在继续学习之前,确保你对本章的内容感到满意是非常重要的。我们建议您在阅读本章时尝试一下这些代码。不要自欺欺人地认为你理解了它,因为你已经读过了;没有什么可以代替你自己输入代码。
要运行代码,我们建议使用 JS 控制台( https://jsconsole.com
),这是 Remy Sharp 的一个工具,允许您执行 JavaScript 并查看结果。一些备选方案是 JS Bin ( http://jsbin.com
)或 JSFiddle ( https://jsfiddle.net
)。您可以在浏览器中输入代码并查看结果。这对短代码行非常有用。图 1-1 显示了 JS 控制台的示例。
img/A310335_2_En_1_Fig1_HTML.jpg)
图 1-1。
The Console tab displays the results of the JavaScript tab using JS bin
对于较大的代码,最好建立一个index.html
页面,并在其中包含您的 JavaScript 文件。这将是你真正把一个网站放在一起的方式。下一节将解释如何做到这一点。在本章中,有几个例子使用了alert()
函数来演示某个变量的值。这纯粹是用来演示概念的。在现实生活中,当您需要检查变量时,您不会使用警报—您会使用浏览器的 JavaScript 控制台。在本章的基本示例中使用警报的原因是它更容易上手。此时,不需要加载开发人员工具,这需要时间来适应。一旦你在本章的后面进入更复杂的代码,你将花时间探索开发者工具。
在网页上使用 JavaScript
在一个典型的 HTML 文件中,通常有两种方式向页面添加 JavaScript。要添加一些 JavaScript,您可以在一个script
标签内内联添加代码,如下所示:
<script type="text/javascript">
//write code here
</script>
或者,您可以创建一个带有.js
文件扩展名的外部 JavaScript 文件,然后通过script
标签加载它:
<script type="text/javascript" src="path/to/your/file.js"></script>
Note
你必须关闭script
标签。
第一个位置在head
元素中,第二个位置就在结束的</body>
标签之前。过去,脚本总是被加载到head
元素中,但是随着性能和页面加载速度比以往任何时候都更加重要,通常建议将脚本放在页面的底部。这也是我们支持的方法。
浏览器从上到下呈现页面,当它遇到您的脚本时,它暂停呈现页面以加载到您的 JS:。因此,页面加载较慢(或者,更重要的是,用户会有这种感觉),因为您加载的 JavaScript 文件阻止了呈现。因此,将脚本放在结束的</body>
标签之前意味着当加载脚本的时候,页面的其余部分已经被加载了。
在开始研究语言本身之前,还有一点需要注意。使用 HTML5 doctype (<!DOCTYPE html>)
,您实际上不需要在脚本标签上定义 type 属性。简单地使用以下代码就足够了:
<script src="path/to/your/file.js"></script>
这在旧浏览器中不会引起问题 HTML5 doctype 也不会——我们强烈推荐使用它。
语法约定
JavaScript 的语法非常基本和清晰,但是在这个过程中您会发现一些微妙之处。通常有不止一种方法来做事情,但是社区有一些随着时间的推移而变得根深蒂固的惯例。
分号
我们想直接提到的一个约定是分号的使用。通常在 JavaScript 中,在行尾添加分号是可选的,你会看到教程没有这样做。然而,惯例是总是在一行的末尾使用分号,这就是我们在本书中要遵循的。显然在某些情况下你不能使用分号,你会在本书中看到,但是在任何分号可选的情况下,我们都会使用分号。我们建议您也这样做。
空格
另一个要考虑的是留白。在 JavaScript 中这是无关紧要的,所以你可以按照你喜欢的方式用空白来布局代码。无论何时你在一组大括号中,你都应该缩进一个制表符,但是除此之外,你会发现自己在适应自己的标准。
评论
在继续之前,在这个阶段值得讨论的意见。JavaScript 允许您在代码中插入注释。这些内容将被忽略,不会被视为代码,因此您可以在注释中添加任何内容。插入注释对于记录代码很有用。注释有两种语法,一种用于单行注释,另一种用于多行注释:
//this is a single-line comment, denoted by two forward slashes
/* this is a multiline comment, started with a slash and an asterisk
and ended with an asterisk and a slash */
使用注释来提醒自己一段代码及其作用,或者为未来的你提供参考。在很长一段时间没有从事代码工作之后,注释确实可以帮助你记住你为什么要写你所写的东西。
变量
通常在编码的时候,你想要保存一些东西的状态。也许你想记住你的背景的当前颜色是红色,或者你刚刚计算的总数是 33。和大多数语言一样,JavaScript 也有变量:存储信息的地方。要创建一个变量,只需用关键字var
声明它,给它命名,然后将其设置为等于某个值。您也可以在不显式设置变量值的情况下声明变量。如果这样做,变量将被设置为undefined
,这是 JavaScript 中的一个特殊值,仅仅意味着这个变量没有被设置为任何值。以下示例声明了三个变量:
var twoPlusThree = 5;
var twoPlusTwo = 2 + 2;
var notYetDefined;
第一个变量twoPlusThree
被设置为值5
。第二个,twoPlusTwo
,被设置为2+2
的结果。在这里你会遇到 JavaScript 的众多操作符之一,+
。这些运算符对值执行运算。大部分都很明显。除了+
(加法),还有–
(减法)、/
(除法)、*
(乘法),还有更多。你会在整本书中遇到更多,所以现在不要太担心他们。第三个变量notYetDefined
没有值,设置为undefined
,因为我们声明了一个变量(也就是我们创建了一个新变量)但是没有设置值。
创建变量
变量可以包含字母、数字和下划线。它们不能以数字开头。因此变量名0abc
无效,而abc0
有效。通常,大多数开发人员不在变量名中使用数字,而是坚持使用字母大小写或下划线符号。
Note
注意我们对变量的命名约定。我们使用的是所谓的 camelCase,这意味着变量名中的第一个单词应该以小写字母开头,但名称中的其他每个单词都应该以大写字母开头。我们将在整本书中使用这个约定。还有其他流行的命名约定,最著名的是 _ 下划线 _ 方法。这使所有单词保持小写,并用下划线分隔。这在其他语言中更流行。大多数 JavaScript 社区都使用 camelCase。
当然,一旦你设置了一个变量的值,并不意味着你不能改变这个值。所有变量的值都可以更改。这与声明变量的方式非常相似,唯一的区别是开头缺少了关键字var
。只有在声明变量的时候才需要。这个例子将totalCost
设置为 5,然后再次将其更新为 5 + 3(显然,您可以将它写成 8):
var totalCost = 5;
totalCost = 5 + 3;
类型
在继续之前,您会注意到,到目前为止,所有变量都被设置为非十进制数。在 JavaScript(和所有编程语言)中,有类型的概念。变量可以是几种类型中的任何一种。最常见的是数字类型和字符串类型。还有布尔型,只能设置为true
或者false
。使用 JavaScript 时,通常不必太担心类型。即使变量是用整数值(例如 5)声明的,也可以将其更新为字符串值,如下所示:
var testVariable = 5; testVariable = "Jack";
这就把testVariable
的类型从整数变成了字符串,JavaScript 一点也不抱怨。除了字符串、数字和布尔值,您需要关注的另外两种类型(目前)是数组和对象。两者都将很快得到更详细的介绍,但是现在,只需要知道数组本质上是一个值的列表。这些值可以是任何类型,并且不是数组中的所有值都必须是同一类型。您可以通过在方括号之间列出值来创建数组,如下所示:
var squares = [1, 4, 9, 16, 25];
var mixed = [1, "Jack", 5, true, 6.5, "Franklin"];
现在,这就是你需要知道的关于数组的全部内容。
另一种类型,object,用一个例子更容易解释。假设您的应用中有一个汽车的概念。这辆车有一定数量的轮子和座位,有一定的颜色,有最大速度。你可以用四个独立的变量来模拟这辆车:
var carWheelCount = 4;
var carColor = "red";
var carSeatCount = 5;
var carMaximumSpeed = 99;
如果只有一个包含所有这些信息的变量——car
——那就更容易了。这就是一个物体的作用。这是一种在一个变量中存储大量信息(通常是相关的)的方法。如果您使用对象,汽车的先前代码可能如下所示:
var car = {
wheelCount: 4,
color: "red",
seatCount: 5,
carMaximumSpeed: 99
};
创建对象的语法与您到目前为止看到的任何语法都有一点不同,所以让我们浏览一下。像平常一样创建变量,但是要创建一个对象,就要用花括号把它括起来。对象是一组键值对,也称为属性。通过以格式key: value
列出它们来创建它们,在除最后一个属性之外的所有属性的末尾加上一个逗号。这是一种更好的以编程方式对代码建模的方式。
要访问对象中的属性,有两种选择:
car.wheelCount;
car["wheelCount"];
使用两种方法访问属性的原因很容易解释。大多数情况下,您将使用第一个版本,点符号。唯一需要使用第二个版本的时候是,如果你需要访问一个对象中的一个键,而这个键的名字存储在一个变量中。在演示中可以更清楚地看到这一点。假设您想要访问的键wheelCount
,由于您的应用中的一些在先代码而存储在一个变量中。如果你想得到wheelCount
的值,你必须使用第二种符号,如下所示:
var keyToGet = "wheelCount";
car[keyToGet]; //this will give us 4
这种情况不经常发生,但是有时候需要用到。在本书的后面,你会看到这样的例子。现在,让我们继续。
功能
一旦你写了一些你可能想在别处再次使用的代码,你有两个选择。当你需要使用它的时候,你可以简单的复制代码——但是这不是一个好的方法。如果你需要改变它,你必须在两个或更多的地方改变它。最好创建一个函数。
创建函数
这使您可以在多个地方重用代码,如果您需要进行更改,您只需在一个地方进行更改。创建一个函数非常简单。使用function
关键字表示您正在创建一个新函数。然后命名它,并将函数代码放在花括号内。
function alertTwo() {
alert("2");
}
这个功能只是在你的屏幕上显示一个显示“2”的警告。注意,函数名后面的括号是空的。这意味着你声明的函数没有任何参数。您可以声明另一个函数,该函数接受一个参数并发出警报,如下所示:
function alertSomething(something) {
alert(something);
}
这个函数通过一个参数传递,这个参数在函数中是一个变量,您可以将其称为something
。您所做的只是提醒该变量的值,如下所示:
alertSomething("Jack");
alertSomething(2);
如果在浏览器中运行这段代码,会弹出两个警告框,第一个显示文本“Jack”。一旦你点击警告框来消除它,另一个包含数字“2”的框将会弹出。
函数也可以接受多个参数,例如:
function alertThings(thing1, thing2) {
alert(thing1);
alert(thing2);
}
alertThings("Jack", "Franklin");
和前面的例子一样,这也给出了两个警告。第一个包含“杰克”,第二个包含“富兰克林”。
jQuery 开发中经常做的事情是将一个对象传递给一个函数,而不是多个变量。调用一个函数并传入多个参数会让人感到困惑;例如:
someFunction("Jack", "Franklin", 1, 2, 3, 4, "a", "x");
所以许多插件 jQuery 广泛使用的东西——将对象传递给函数。例如,如果您要声明一个带有三到四个或更多参数的函数,您可能会让该函数接受一个对象,如下所示:
function aPerson(person) {
alert(person.firstName);
alert(person.lastName);
alert(person.age);
}
var jack = {
firstName: "Jack",
lastName: "Franklin",
age: 20
}
aPerson(jack);
如果运行该代码,您将看到三个警告,每个警告都警告存储在jack
变量中的对象的属性。这是在大量使用 jQuery 时使用的一种模式,所以一定要理解这里发生了什么。为了避免向函数传递大量参数——这使得很难记住哪个参数是哪个参数以及它们进入的顺序——开发人员通常编写他们的函数来接受一个对象作为唯一的参数。这意味着每个参数都可以被命名——顺序并不重要——作为开发人员,查看代码并了解发生了什么要容易得多。
与其现在讨论函数及其所有细节,不如在后面的章节中讨论它们。然而,在继续之前,您需要理解函数返回值的概念。
返回值的函数
函数通常用作执行某些计算的方法,例如将英寸转换为厘米。这是一个你期望传入一个值的函数,它计算并“返回”一个值。以下示例显示了如何实现这一点:
function inchesToCM(inches) {
return inches * 2.54;
}
var sixFeetInInches = 72;
var sixFeetInCM = inchesToCM(sixFeetInInches);
这就剩下sixFeetInCM
为 182.88,也就是 72 乘以 2.54。给sixFeetInCM
变量赋予那个值的原因是因为inchesToCM()
函数返回它的自变量——英寸——乘以 2.54。通过返回参数,sixFeetInCM
变量被设置为inches * 2.54
给你的值。
函数绝对可以返回任何值。通常,您可能希望返回一个布尔值true
或false
,如下所示:
function isItSunnyInBritain() {
return false;
}
var isSunny = isItSunnyInBritain();
这个函数将返回false
,这是应该的。面对现实吧,英国从来都不是晴天!从函数返回值是你会经常用到的。
条件式
您经常想做的事情是有条件地运行代码。也就是说,只有在其他事情为真或为假的情况下才做某事。例如,如果age
变量小于 12,则警告“孩子”。JavaScript 通过if
语句拥有这种能力:
var age = 10;
if(age < 12) {
alert("Child");
}
但是如果年龄大于 12 岁,你想做别的事情呢?除了if
语句,您还可以在它的末尾附加一个else
,如下所示:
var age = 15;
if(age < 12) {
alert("Child");
} else {
alert("Not a child");
}
这里您遇到了另一个操作符—小于符号,<
。还有它的反义词,大于,>
,还有“小于等于”和“大于等于”,<=
和>=
。如果你想检查多个条件,你也可以使用else if
,就像这样:
if(age <= 12) {
alert("Child");
} else if (age < 20) {
alert("Teenager");
} else {
alert("Adult");
}
当然,如果需要,您可以使用多个else if
语句,但是通常您不需要多于一个或者两个。任何可以评估为true
或false
的东西都可以作为if
语句的条件。一个更简单的方法是想象把一些陈述放在这些括号里,在你的头脑中计算这个陈述是对还是错。如果你能做到这一点,你的条件可以用在一个if
语句中。
var name = "Jack";
var age = 20;
if(age > 18 && name === "Jack") {
alert("Hello Jack, you’re older than 18!");
}
这里有两件新的事情要讨论。首先,您用 and 运算符将两个条件组合成一个,&&.
这意味着只有当条件的左右两边都计算为true
时,条件才会计算为true
。
其次,您已经看到了如何检查等式。在 JavaScript 中,这是一个复杂的领域。您可以同时使用==
和===
来检查相等性,两者都有细微但重要的区别。现在,当我们告诉你总是使用===
时,请相信我们。
除了&&
,还有||
,它是“或”操作符。让我们来看看实际情况:
var age = 19;
var name = "bob"; if(age > 18 || name === "Jack") {
alert("your name is Jack or you’re older than 18");
}
即使只有一个条件语句为真,警报仍会显示在这里。年龄确实大于 18,这使得这个人的名字不是 Jack 无关紧要,因为只要满足其中一个条件,or 运算符就会返回true
。
确保你理解了||
和&&.
之间的区别,如果两个条件中的任何一个计算为true
,那么第一个计算为true
;而如果两个条件都评估为true
,则&&
评估为true
。
也可以否定条件句,也就是说,如果相反的情况成立,它们就通过了,如下所示:
var age = 20;
if(!age < 18) {
alert("Hello adult");
}
求反运算符!
反转条件运算的结果。在这个例子中,age < 18
是false
,但是前缀为条件反转false
到true
的!
。
一般来说,你应该尽量避免像前面那样的否定,把它写成age >= 18
而不是!age < 18
,因为这样代码更容易阅读。扫描代码和评估其功能越快越好。
使用控制台调试
前面,我们简要地提到了浏览器中可用的开发人员控制台。我们说过,一旦我们遇到更复杂的例子,我们将从使用alert()
切换到使用console.log()
。在进行这种转换之前,您需要查看一下您可以使用的调试。
现代浏览器附带了一个 JavaScript 控制台,这是 JavaScript 开发人员宝库中的一个无价工具。下表描述了如何在所有现代浏览器中访问控制台:
- IE10+:按 F12 并单击控制台选项卡。
- chrome:MAC OS 上的 Alt+Cmd+J。Windows 上的 Ctrl+Shift+J。
- safari:MAC OS 上的 Alt+Cmd+I。Windows 上的 Ctrl+Alt+I。
- 火狐:macOS 上的 Alt+Cmd+K。Windows 上的 Ctrl+Shift+K。
- opera:MAC OS 上的 Alt+Cmd+I。Windows 上的 Ctrl+Shift+I。
我使用谷歌 Chrome 作为我的首选浏览器,本书中的所有截图都来自 Chrome 的控制台(除非另有说明),但所有浏览器都有非常相似的功能集,它们看起来都一样,所以选择一个最适合你的。请看图 1-2 中的例子。
img/A310335_2_En_1_Fig2_HTML.jpg)
图 1-2。
After declaring a variable, viewing its value in Google Chrome’s JS console
控制台非常适合尝试代码片段,但它更适合调试。最流行的方法是console.log()
,它会将数据记录到控制台供您查看。从这一章开始,示例使用这种方法,而不是alert()
。当处理复杂的数据结构时,console.log()
提供了一种更好的查看变量值的方式。
要查看示例,请创建以下 HTML 文件(将其命名为 sensible ),然后使用开发人员工具在浏览器中打开它:
<!DOCTYPE html>
<html>
<head>
<title>Hey</title>
<script type="text/javascript" charset="utf-8">
console.log("Jack");
</script>
</head>
<body>
</body>
</html>
如果您按照前面的说明打开开发人员控制台,您应该会看到类似图 1-3 的内容。
img/A310335_2_En_1_Fig3_HTML.jpg)
图 1-3。
The string “Jack” being logged to the console
你可以把任何事情记录到控制台,它会知道如何处理。当您深入研究数组时,您将会看到这一点。
数组
在继续学习 jQuery 之前,了解数组是很重要的。如前所述,数组只是一个值的列表。下面是一个数组的示例:
var classMates = ["Jack", "Jamie", "Rich", "Will"];
这差不多就是前面所介绍的内容,所以现在是时候深入研究了。
您可以通过在变量后的方括号中添加一个数字来访问数组中的单个元素,如下所示:
classMates[1]; //Jamie
注意这里位置 1 的元素不是“Jack”,而是“Jamie”。这是因为数组是零索引的。也就是说,数组中的第一个元素实际上位于位置 0,而不是位置 1。如果你不是程序员,这可能需要一些时间来适应,但是一旦你掌握了它,它就会成为你的第二天性。因此,要从数组中获取名称“Jack”,您需要使用classMates[0]
。您可以使用classMates.length
找出数组的长度,在本例中返回 4。作为一个快速的测试,当你不知道数组的长度时,你认为如何得到数组的最后一个元素呢?
你应该这样做:
classMates[classMates.length - 1]; // "Will"
看看你是否能在不先阅读解释的情况下弄清楚这是如何工作的。classMates.length
给出数组长度,即 4。因此,要获取数组中的最后一项,需要获取最后一个索引处的 person,即长度减一,直到第一个元素位于位置 0 而不是位置 1。
记住,JavaScript 数组中可以包含任何东西,包括对象和其他数组。这就是你所谓的二维数组,数组中的每个元素本身就是一个数组:
var twoDArray = [
["Jack", "Jon", "Fred"],
["Sue", "Heather", "Amy"]
];
要访问数组的数组中的元素,使用方括号符号,就像您之前使用的那样,来获取classMates
数组中的第二个元素,classMates[1]
:
twoDArray[0][0]; //Jack
twoDArray[1][0]; //Sue
twoDArray[1][2]; //Amy
第一组方括号抓取了twoDArray
的元素,所以twoDArray[0]
返回包含"Jack", "Jon",
和"Fred"
的数组。twoDArray[1]
是包含"Sue", "Heather",
和"Amy"
的数组。
这并不是你必须经常做的事情,但是在这个 JavaScript 介绍中向你展示是值得的,因为它确实让你理解了数组的基础。
要向数组中添加元素,使用push()
方法:
classMates.push("Catherine");
注意push()
总是会在数组的末尾添加一个元素。
不幸的是,没有这样的方法可以轻松地删除数组中的项目。您可以使用delete
操作符,乍一看,它可以完成您需要的一切:
delete classMates[1]
虽然这看起来可行,但实际上并不可行。如果您对初始数组“Jack”、“Jamie”、“Rich”、“Will”执行该命令,将会发生以下情况:
delete classMates[1];
console.log(classMates); //["Jack", undefined, "Rich", "Will"]
这是delete
的关键之处:它不从数组中移除元素。它只是用undefined
替换该索引处的值。因此,要真正从数组中完全删除一个元素,还需要做更多的工作。当这个问题实际发生时,您将在本书的后面部分重新讨论这个问题。
环
现在,您已经了解了使用数组的基本知识,可以开始学习循环了。很自然的,一旦你有了一个条目列表,你经常想要依次检查每一个条目,并对其执行一些计算或功能。您将在这里遇到的两个循环是while
循环和for
循环。
while
循环非常简单,实际上采用了您已经看到的形式,即if
语句。基本的while
循环如下所示:
while(condition) {
//code
}
当条件评估为true
时,大括号内的代码将继续执行。这有许多用例,但最常见的是用于遍历一个列表,就像这样:
var count = 0;
while(count < classMates.length) {
alert(classMates[count]);
count++;
}
如果您要运行该代码,您将得到五个警报—“Jack”、“Jamie”等等,分别对应于classMates
数组中的五个项目(在前面的示例中,您使用了push()
方法来添加第五个项目,“Catherine”)。一行一行地看,它是这样工作的:
- 首先,将一个新的
count
变量设置为 0。 - 代码执行的条件是
count
变量必须小于classMates.length
的长度。 - 如果是,你要做两件事:
- 首先警告
classMates[count]
处的值,它将是classMates[0]
,然后是classMates[1]
,直到classMates[3]
——最后一次count
变量小于classMates
的长度。 - 第二,运行
count++
,这是一个你没见过的新操作符。这只是count = count + 1
的一个快捷方式,所以它将count
变量加 1。
- 首先警告
你会发现自己经常使用while
循环。当然,它不一定要和数组一起使用——你可以在没有数组的情况下使用它,但是它会导致一个无限循环,所以我们不建议运行它。这里有一个例子:
while(1 < 5) {
alert("hello");
}
在这里,条件 1 < 5 将始终为真,因此循环内的代码将被反复执行。大多数现代浏览器会检测到这一点,并防止代码使浏览器崩溃,但即使如此,我们也不建议运行它。
除了while
循环,还有一个for
循环。其语法略有不同:
for(before loop; condition; iteration) {
//code
}
在一个for
循环的参数中,您定义了三件事:
- 循环开始前要运行的代码
- 大括号内的代码可以执行所必须满足的条件
- 每次迭代结束时运行的代码
最好用一个例子来说明这一点。下面将显示数字 0 到 9:
for(var i = 0; i < 10; i++) {
alert(i);
}
如果您想使用一个for
循环而不是一个while
来循环通过classMates
数组,可以这样做:
for(var i = 0; i < classMates.length; i++) {
alert(classMates[i]);
}
将此与while
循环进行比较:
var count = 0;
while(count < classMates.length) {
alert(classMates[count]);
count++;
}
唯一的区别是首字母var count = 0;
被移到了for
的括号内,而count++
被移到了括号的末尾。开发人员通常会使用count
作为变量来循环一些代码;其他时候,你会看到在“迭代器”中使用了i
当然,您可以使用任何您喜欢的变量名,但这两个往往是最受欢迎的。我们将在本书的大部分内容中使用i
,但是如果你喜欢更冗长的count
,或者任何类似的东西,请随意使用。
使用while
循环或for
循环,可以在循环时编辑数组的值,如下所示:
var i = 0;
while(i < classMates.length) {
classMates [i] = "Class Mate " + i;
i++;
}
这会将您的classMates
数组更新为
["Class Mate 0", "Class Mate 1", "Class Mate 2", "Class Mate 3"]
在本章结束之前,还有一件关于for
循环的事情你需要知道。当处理一个对象时,你可以结合使用for
循环和in
操作符来循环属性:
var classMates = {
"Jamie" : 20,
"Will": 21,
"Rich": 22,
"Jack": 23
}
for(classMate in classMates) {
console.log(classMate + " is " + classMates[classMate] + " years old");
}
这将为您提供以下输出:
Jamie is 20 years old
Will is 21 years old
Rich is 22 years old
Jack is 23 years old
这里的关键是第一行,for(classMate in classMates) {}
。这将遍历classMates
对象,并遍历对象中的每个属性。然后你可以通过classMates[classMate]
得到那个属性的值。
More console.log()
您在查看数组时使用了console.log()
,但是到目前为止,您只在最基本的形式中使用了它,通过向它传递一个参数,您希望它记录到控制台。比那强大多了。您可以传入多个参数,它会在同一行记录所有参数。例如:
var classMates = ["Jack", "Jamie", "Rich", "Will"];
var twoPlusTwo = 4;
console.log(classMates);
console.log("twoPlusTwo", twoPlusTwo);
您将看到如图 1-4 所示的输出。
img/A310335_2_En_1_Fig4_HTML.jpg)
图 1-4。
The console logging out your array and variable
您可以看到,注销classMates
数组可以完全清楚它包含的内容,这正是console.log()
存在的目的。如果希望在一行中输出多种内容,可以通过向函数传递多个参数来轻松实现。第二个例子注销字符串"twoPlusTwo"
,然后注销变量twoPlusTwo
。我们经常在记录大量值时这样做,所以在控制台中哪一行记录了什么就更清楚了。我们将在本书中大量使用console.log()
。
摘要
本章涵盖了很多 JavaScript 基础知识,包括变量、if
语句、循环、数组、对象等等,现在您已经有了坚实的基础。当您进入 jQuery 时,我们会定期停下来,以确保您对所做工作背后的 JavaScript 感到满意。系好安全带,因为在下一章中,是时候继续前进,认识 jQuery 了。
二、jQuery 的基础知识
jQuery 是一个强大而复杂的库,于 2006 年 8 月首次发布,尽管最初的想法出现得更早。在开始之前,我们有时间上一堂简短的历史课,介绍图书馆是如何产生的。
第一次在网上发布任何暗示潜在图书馆正在形成的消息是在 2005 年 8 月 22 日。jQuery 的创始人约翰·瑞西格(John Resig)发布了一篇名为“JavaScript 中的选择器”( https://johnresig.com/blog/selectors-in-javascript/
)的博文,展示了瑞西格的想法,即我们可以使用 CSS 选择器与 JavaScript 中的元素进行交互。这展示了一个新的想法,最终形成了我们今天所知道和喜爱的图书馆的雏形。jQuery 于 2006 年 1 月在纽约的 Bar Camp 正式发布,并迅速风靡互联网,占据了许多热门网站的首页。jQuery 不断发展,并在 2006 年 8 月达到稳定的 v1。从那以后,它继续增长。它对 web 开发的影响不可低估,对 JavaScript 的社区观点的影响更为重要。
在本章中,您将执行以下操作:
- 看看浏览器如何通过文档对象模型(DOM)表示网页。
- 看看 web 页面上下文中的 DOM 节点和术语父节点、子节点和兄弟节点。
- 下载 jQuery 源代码并将其包含在网页中。
- 编写一些利用 jQuery 的代码。
- 详细探究代码是如何工作的,并了解 jQuery 的一些特性。
- 探索 jQuery API 文档以及如何使用它来回答您可能遇到的任何问题。
jQuery 让“普通”开发人员更容易理解 JavaScript。例如,在通过 ID 选择元素时,您更喜欢以下两种语法中的哪一种?
document.getElementById("example");
或者
$("#example");
突然,如果您知道如何用 CSS 选择元素,您可以通过使用 jQuery 将这些知识转移到 JavaScript。jQuery 提供了一种与文档对象模型交互的可靠的跨浏览器方法。在我们继续深入之前,是时候讨论 DOM 了。
文档对象模型(DOM)
当你浏览一个网站时,你会看到许多元素组合在一起,形成你面前的东西。为了能够通过代码访问这些元素来删除、添加和操作它们,您需要某种形式的接口——页面上元素的表示,它是结构化的,并遵循一组关于如何对它们建模的规则。这就是大教堂。DOM 还允许您捕获浏览器事件——比如用户点击链接、提交表单或向下滚动页面。在第三章中,你将看到如何使用 jQuery 来遍历 DOM。
在网络和浏览器的早期,JavaScript 实现的标准并不十分清晰。这导致浏览器以不同的方式实现功能,这给开发人员带来了问题。这导致任何 JavaScript 都必须为具有不同实现的不同浏览器编写多次,主要是 Netscape 和 Internet Explorer (IE)。
幸运的是,随着事情的进展,浏览器采用了相同的标准,事情也解决了。然而,浏览器支持 DOM 的水平在今天仍然会引起问题。特别是,我们不能摆脱旧版本的 Internet Explorer,它不支持 DOM 到更现代的浏览器的水平。这是 jQuery 如此有价值的一个原因:它提供的一切在旧版本的 IE 中都能很好地工作,就像在最新版本的 Google Chrome 或 Mozilla Firefox 中一样。需要注意的是,IE 的最新版本是 11;微软 Edge 现在是 Windows 10 的默认浏览器。
在继续使用 jQuery 之前(您很快就会明白了!),有必要花点时间介绍一下 DOM 是如何工作的。当一个页面被加载时,浏览器生成一个页面内容的表示,对于每个元素,它生成一个或多个表示它的节点。节点有多种类型,如果这是一本纯粹关于 DOM 与 JavaScript 交互的书,我们将会更详细地讨论 DOM。
正如我们在第一章中提到的,我们认为给刚接触 jQuery 的人一个坚实的 jQuery 基础介绍是非常重要的。我们已经非常详细地介绍了 JavaScript,我们觉得看一下 DOM 很重要。当浏览器将当前页面表示为 DOM 时,每个元素都是一个节点。假设您有一个包含一些文本的段落,例如:
<p>Hello World</p>
那不是一个节点,而是两个节点。有一个包含“Hello World”的文本节点和一个段落的元素节点。
Note
文本节点将是元素节点的子节点,因为它驻留在元素节点中。在一个典型的页面中,有许多嵌套的节点。
包含两个段落且两个段落中都有文本的,div
的结构如下:
div element node
-- paragraph element node
---- text node
-- paragraph element node
---- text node
这个实例中的两个段落是兄弟,因为它们有相同的父节点。段落是div
的子节点,但是文本节点不是子节点,因为它们不是div
元素的直接后代。它们是段落节点的子节点。您需要了解三种主要的节点类型:元素、文本和属性节点。假设你给了段落一个类,比如:
<p class="intro">Hello World</p>
现在有三个节点在起作用:
- 代表段落的元素节点
- 包含文本“Hello World”的文本节点
- 表示该元素的属性节点有
class="intro"
有点令人困惑的是,属性节点不被认为是元素节点的子节点。
在这些元素之间,它们构成了绝大多数网页的绝大部分。在(最后)开始学习 jQuery 之前,请确保您理解了以下术语,因为它们在本书中会不断出现:
- 子节点:是另一个节点的直接后代的节点,通常是元素节点
- 父节点:具有直接后代的节点(例如,子节点)
- 兄弟节点:共享同一父节点的两个节点
最后再重复一次,下面是一个可视化的表示:
div parent
-- p child of div, sibling of p
---- "hello world" - child of p
-- p child of div, sibling of p
---- strong child of p
------ "hello" child of strong
当我们在本书后面讨论用 jQuery 选择元素时,理解术语“子元素”、“父元素”和“兄弟元素”将非常重要,所以请确保您对它们的含义有信心。
正在下载 jQuery
在做了大量准备之后,您已经准备好开始第一次使用 jQuery 了。最好的起点是位于 http://jquery.com
的 jQuery 网站(见图 2-1 )。
img/A310335_2_En_2_Fig1_HTML.jpg)
图 2-1。
The jQuery home page
单击 jQuery 主页上的大下载 jQuery 按钮(或下载选项卡)打开下载页面。这里给出了将 jQuery 添加到项目中的许多方法。例如,如果您想在本地添加 jQuery,您可以下载压缩版本或未压缩版本。如果您正在使用 npm(节点包管理器)或 Bower(另一个包管理器),您可以在这里找到说明。此外,还有使用 CDN(内容交付网络)和其他一些方式的说明。
如果您单击主页上的“下载 jQuery”按钮。下载 jQuery 有多种选择。下载页面上列出了两个压缩级别选项:
- 生产(87KB),缩小并压缩
- 开发(268KB),未压缩代码
除非您想详细研究使用 jQuery 创建的每个项目的 jQuery 源代码,否则请始终选择产品版本。这段代码是通过一个 minifier 运行的,mini fier 是一个将 JavaScript 文件压缩成较小版本的程序。缩小器执行许多操作来使代码尽可能小,包括
- 去掉所有的空白。
- 删除所有注释。
- 重命名长变量名;例如,
var myCar
可能会变成var a
。
精简的代码是完全不可读的,但它并不是为了可读而设计的——它的设计是为了使文件尽可能小。从现在开始,当我们提到 jQuery 源代码时,我们指的是 jQuery 的缩小版。
一些开发者链接到一个 CDN 托管版本的 jQuery,其中最流行的是谷歌的 CDN ( https://developers.google.com/speed/libraries/#jquery
)。这些允许您通过引用驻留在 CDN 上的 jQuery 文件来包含 jQuery。如果您想包含来自 Google CDN 的最新版本的 jQuery,您可以这样做:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
这样做有好处。如果用户访问了以这种方式引用 jQuery 的另一个站点,他们可能已经缓存了该文件,这意味着浏览器不必再次下载它。然而,对于本书中的示例,我们选择在本地下载一个版本的 jQuery,原因很简单:您不需要通过互联网来完成这些示例。这听起来可能很愚蠢,但不止一次,我们的一位作者在火车上准备做一些工作,只记得他参考了 Google CDN 版本的 jQuery,而且他没有互联网连接。
jQuery API 文档
如果您正在使用 jQuery,您需要一个很好的来源来了解每个 API 的功能。jQuery 文档( http://api.jquery.com
)列出了 jQuery 提供的每一种方法。jQuery 如此成功的另一个原因是它的文档,这很棒。我们不能夸大我们认为文档有多好(见图 2-2 )。
img/A310335_2_En_2_Fig2_HTML.jpg)
图 2-2。
The jQuery API index page
有几种方法可以在网站上找到你想要的东西。如果你确切地知道你想要哪种方法,使用位于屏幕右上角的搜索框是最快的方法。如果您不确定您到底想要什么——也许您正在寻找一种方法来做一些特定的事情,但是您不确定它是否存在——您可以浏览屏幕左侧列出的 jQuery API 类别来缩小搜索范围。你现在还不需要看这些,但是你会多次回到 API。把它放在你的书签栏上,或者找一个简单的方法浏览它,因为你会经常用到它。
编写一些 jQuery
将下载的 jQuery 文件作为jquery.js
保存在机器上的新文件夹中。您还将向该文件夹添加一个 HTML 文件,因此也创建一个index.html
页面。最后,您想在一个单独的文件中编写所有的 JavaScript,所以创建app.js
。此代码在02/code/ex1
内可用。
在您选择的编辑器中加载 HTML 页面——我个人使用 Vim 我们强烈推荐 Sublime Text 2 ( www.sublimetext.com/2
)、Visual Studio Code ( https://code.visualstudio.com
)或 Atom ( https://atom.io
),所有这些都可以在 Windows、macOS 和 Linux 上运行——并添加以下内容:
<!DOCTYPE html>
<html>
<head>
<title>Chapter 02, Exercise 01</title>
<script src="jquery.js"></script>
<script src="app.js"></script>
</head>
<body>
<p>Hello World</p>
</body>
</html>
这只是一个基本的 HTML 页面,没有什么花哨的。看看这两个<script>
标签
<script src="jquery.js"></script>
<script src="app.js"></script>
先载入 jQuery 再载入app.js
文件,目前为空。
Note
使用script
标签加载文件的顺序非常重要。请记住,您编写的代码将依赖于 jQuery,因此您必须在任何使用它的脚本之前加载 jQuery。
现在你有了自己的页面,继续在浏览器中加载index.html
。除了“Hello World”文本之外,您还看不到任何内容。进入app.js
并添加下面一行——你在书中写的 jQuery 的第一行!
$("body").css("background", "red");
你能猜到这是干什么的吗?你已经看到了$("body")
选择了"body"
标签(记住,它们只是 CSS 选择器),你可能会尝试一下css("background", "red")
做什么。刷新页面,你…不会看到任何变化。
这是许多 jQuery 初学者在开始时都会犯的错误。问题又回到了您的index.html
文件中:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Chapter 02, Exercise 01</title>
<script src="jquery.js"></script>
<script src="app.js"></script>
</head>
<body>
<p>Hello World</p>
</body>
</html>
在加载页面的其余部分之前,先加载 JavaScript,所以当执行 JavaScript 时,页面还没有完全加载,这意味着 DOM 还没有准备好。因为在运行 JavaScript 时页面没有完全加载,所以浏览器没有完成 DOM 的构建,这意味着就 DOM 而言,在运行 JavaScript 时,"body"
并不存在。您有两种选择:
- 在页面底部包含您的 JavaScript,就在关闭
</body>
之前。这意味着它在 DOM 加载后运行。 - 告诉您的 JavaScript 在 DOM 准备好之前不要执行。
实际上,最好在底部包含 JavaScript,这样就不会延迟内容加载。所以在这本书的大部分时间里,我们都会这么做。然而,这一次,我们将选择第二个选项——纯粹是因为我们需要解释你如何着手去做。为了讨论如何在 DOM 加载之前停止代码的运行,我们将简单讨论一下事件。我们将在第四章中详细介绍这些事件,但是你现在需要尝试一下这个话题。
在浏览器中,编写 JavaScript 是非常基于事件的。编写基于事件执行的代码。用户单击一个按钮,向下滚动页面,悬停在一个图像上,等等。这些动作中的每一个都会引发一个事件,这个事件被 JavaScript 捕获,然后基于事件的发生执行代码。
加载 DOM 时,浏览器也会发出一个事件。然后,您可以编写仅在该事件触发时执行的代码,这意味着您知道您的代码将仅在 DOM 设置完毕并准备就绪时执行。使用 jQuery,您可以这样做:
$(function() {
//DOM is ready to go
});
让我们来分解这条线:
-
$(document)
:这将变量document
传递给 jQuery。document
变量是一个特殊的变量,它包含对页面上所有 HTML 元素的引用。当这个对象触发一个ready
事件时,您想要执行一些代码。 -
.ready()
:ready
是 jQuery 支持的众多事件之一。你传递给它一个函数,这个函数在ready
事件被触发时被执行。因为你正在做$(document).ready()
,当一个ready
事件在document
对象上注册时,你传入的函数被触发。 -
function() {}
:您传递给ready
调用的是一个常规的 JavaScript 函数,当事件发出时,它将被调用。这就像创建函数,就像你在第一章中所做的那样,但是你没有命名它们,而是直接传入一个函数。你可以这样做:function onReady() { alert("READY TO GO!"); } $(document).ready(onReady);
-
但在实践中,更简单的方法是创建函数并立即将其传递给事件处理程序,而不需要先显式命名它。这样,你就创建了一个匿名函数,一个没有名字的函数。
前面一行代表以下所有内容:
$(function() {
$("body").css("background", "red");
});
当你刷新index.html
时,你会看到一个红色的背景!现在,做$(document).ready(function() {})
是如此普遍,jQuery 有一个方便的快捷方式。您可以简单地执行以下操作:
$(function() {
});
这意味着完全相同的事情。如果 jQuery 检测到您已经向它传递了一个函数,它会认为这个函数应该已经在 DOM 上执行了。这是一个方便的小快捷方式,可以帮你节省少量的打字时间。
上一段中的短语“jQuery 检测到您已经向它传递了一个函数”意味着当您选择类似于$("body");
的东西时,您实际上正在做的是调用 jQuery 提供的一个函数,该函数被存储为一个名为$
符号的变量。你也可以轻松做到这一点。下面的代码是有效的,因为 JavaScript 不介意变量中有$
符号或者变量名只有一个字符长:
var $ = function() { console.log("hey"); };
$(); //logs "hey" to the console
Note
如果页面上有 jQuery,就不要这样做,因为您将覆盖$
变量——这意味着它将不再引用 jQuery。
所以 jQuery 所做的就是将一个函数绑定到$
,这很聪明。它可以检测您传递给它的内容,并执行某些操作。所以当你传入$("body");
时,它知道选择 body 元素。但是当你传入$(function() {})
;
时,它会检测到你传入了一个函数,并相应地采取行动。
现在,尝试一些更复杂的东西。在 HTML 页面的<head>
中,添加一个到新样式表的链接,您也应该创建这个样式表,如下所示:
<link rel="stylesheet" type="text/css" href="style.css" />
删除 Hello World 段落,并在<body>
标记中用以下 HTML 替换它:
<div id="box">my box</div>
进入style.css
并添加以下内容:
#box {
width: 100px;
height: 100px;
text-align: center;
background: #f00;
font-size: 14px;
}
最后,将您的app.js
编辑成简单的:
$(function() {
});
您应该会看到一个简单的屏幕,如图 2-3 所示。
img/A310335_2_En_2_Fig3_HTML.jpg)
图 2-3。
The resulting box
它不会赢得任何设计奖项,但现在你可以用这个盒子做一些有趣的东西。首先,创建一个变量来存储对 ID 为"box"
的div
的引用,如下所示:
var box = $("#box");
将对它的引用保存为变量,因为您将不止一次使用它。执行以下操作是低效的,因为每次使用$("#box")
时,都要让 jQuery 选择元素两次:
$("#box").doSomething();
$("#box").doSomethingElse();
最好只做一次,然后保存到一个变量中。
动画示例
现在让我们看一个 jQuery 动画示例。虽然动画可能看起来令人望而生畏,特别是在开始时,但它是真正展示 jQuery 能力并立即给出结果的领域之一,这使得它成为一个很好的起点。这个例子不会涉及太多的细节,只是浏览一下 jQuery 的一些关键特性。稍后,您将详细了解每个领域。这纯粹是对 jQuery 功能的简单介绍。
你要做的第一件事是淡出你的盒子。使您的app.js
文件看起来如下:
$(function() {
var box = $("#box");
box.fadeOut("slow");
});
刷新你的页面——那个可爱的红框会慢慢淡出你的视线。很容易理解为什么fadeOut()
方法被恰当地命名。正如你所看到的,传入参数"slow"
使盒子淡出得更慢。你也可以使用关键字"normal"
和"fast"
,它们的功能和你想象的完全一样。
如果您想要一个全面概述fadeOut
方法如何工作以及如何使用它的页面,请查阅 jQuery API 文档。如果您搜索 fadeOut 并找到该方法的文档,您会看到如图 2-4 所示的内容。
img/A310335_2_En_2_Fig4_HTML.jpg)
图 2-4。
The jQuery documentation for the fadeOut() method
文档的第一部分如下:
.fadeOut( [duration] [, complete] )
duration A string or number determining how long the animation will run.
complete A function to call once the animation is complete.
能够阅读和理解 API 将为您节省大量时间。前面的语法现在可能还很陌生,但是一旦你知道它是如何工作的,就很容易理解了,因为它在整个 API 中都是一致的。第一行描述了如何调用该方法。这表明您可以通过传入一个持续时间和一个完成函数来调用fadeOut()
。每个参数周围的方括号表示该参数是可选的,您不必传递任何一个参数。您可以传入一个参数,传入两个参数,或者一个都不传,jQuery 知道如何处理这种情况。之前,您这样称呼它:
$("#box").fadeOut("slow");
您可以看到您传入了持续时间,但没有传入完成方法。回调是 JavaScript 中经常使用的一个术语,指的是一旦某个东西执行完毕就被调用的函数。在fadeOut()
的上下文中,这个完成函数会在你的盒子淡出后被调用。要查看实际效果,请将您的app.js
更改为以下内容:
$(function() {
var box = $("#box");
box.fadeOut("slow", function() {
alert("box finished fading out");
});
});
一旦该框淡出,您将在屏幕上看到一个警告。这给了你很大的力量去做一些事情,然后在最初的事情完成的时候运行其他的事情。回调在 jQuery 中被广泛使用。大量的方法,尤其是动画方法,都需要回调,你会经常用到它们。当然,因为两个参数都是可选的,所以您也可以只传入一个回调,就像这样:
$(function() {
var box = $("#box");
box.fadeOut(function() {
alert("box finished fading out");
});
});
你会注意到当你刷新的时候,这个盒子会更快的消失。以前,你以“慢”的速度通过。但是如果没有传入一个会怎么样呢?jQuery 是做什么的?
任何可选的参数都有一个默认值,jQuery API 会告诉你它是什么。以fadeOut()
为例,jQuery API 说:
Duration is given in milliseconds; Higher values indicate slower animations, not faster ones. You can provide the strings “fast” and “slow” to represent the durations of 200 and 600 milliseconds, respectively. If any other string is provided or the duration parameter is omitted, the default duration of 400 milliseconds is used. (
http://api.jquery.com/fadeOut/
)
所以如果你遗漏了一个参数,它默认为 400 毫秒。传入“slow”是将它设置为 600 毫秒。你也可以传入一个数字。试着慢慢来。记住,数字是毫秒,所以 3 秒= 3000 毫秒。
$(function() {
var box = $("#box");
box.fadeOut(3000, function() {
alert("box finished fading out");
});
});
所以有了fadeOut()
,jQuery 就有了它能识别的三个默认字符串:
"slow"
: 600 毫秒"normal"
: 400 毫秒(也是默认)"fast"
: 200 毫秒
除此之外,您可以以毫秒为单位传入一个值。
现在假设您想要连续地淡入淡出一个框,可能需要十次。你不会惊讶地发现fadeOut()
有一个同伴fadeIn()
,它做的正好相反。所以,你可以把这两种方法结合起来,得到想要的效果。至少,如果您没有正确地浏览 API,您会这样做。
你看,除了fadeIn()
和fadeOut()
,还有fadeToggle()
。如果它不可见,它将在框中淡入,如果它可见,它将淡出框。因此,你可以利用这一点使事情变得容易得多。如果您在文档中搜索了“fade ”,那么您已经看到了这个方法。我们怎么鼓励你广泛使用 API 文档都不为过,尤其是在学习的时候。
img/A310335_2_En_2_Fig5_HTML.jpg)
图 2-5。
The API documentation search results for “fade”
所以,你要做的是:
- 创建一个函数,将切换框,然后退出。
- 存储一个变量,记录你这样做的次数。
- 有一个段落,它的文本总是更新为框淡入淡出的次数。
然后,该函数将调用自身,使该框再次淡入淡出——如果保持计数的变量小于某个数量。
这里会有几个新东西,耐心点。最后,您将会看到 jQuery 让事情变得多么简单,这是非常令人兴奋的。你是从上一个练习停止的地方继续,所以你不是完全从头开始。
首先要做的是将段落添加到您的index.html
页面,看起来应该如下:
<!DOCTYPE html>
<html>
<head>
<title>Chapter 02, Exercise 02</title>
<script src="jquery.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div id="box">my box</div>
<p></p>
</body>
</html>
添加一个空的 HTML 元素并不是一个好的做法,但是在不久的将来,你将学会如何避免这个问题。编辑app.js
,设置将要使用的变量,如下所示:
$(function() {
var box = $("#box");
var para = $("p");
var i = 0;
});
您将存储对框、段落和count
变量i
的引用,该变量被设置为 0。首先要做的是让你的段落显示计数器的值。要更新元素中的文本,可以使用text()
方法。如果您不带参数调用text()
,将会返回文本。如果你传入一个参数,它会将文本设置为你传入的内容。因此,你可以做para.text(i);
来设置文本的值为i
。
现在,您必须编写主函数来执行所有的切换。与其一行一行地做这件事,不如直接查看完整的代码,然后遍历它。您的app.js
文件将如下所示:
$(function() {
var box = $("#box");
var para = $("p");
var i = 0;
para.text(i);
function toggleBox(i) {
box.fadeToggle(500, function() {
i = i + 1;
if(i < 10) {
para.text(i);
toggleBox(i);
};
});
};
toggleBox(i);
});
先说一下toggleBox()
功能:
function toggleBox(i) {
box.fadeToggle(500, function() {
i = i++;
if(i < 10) {
para.text(i);
toggleBox(i);
};
});
};
你做的第一件事是调用fadeToggle()
,它将根据当前状态淡入或淡出盒子。就像fadeIn()
和fadeOut()
一样,给它一个速度——半秒(500 毫秒)——和一个回调函数,一旦盒子淡入/淡出就执行这个函数。该函数采用一个参数i
变量,它存储已经执行的渐变次数。你需要这个来看看你是否应该继续褪色。
在回调中,执行以下操作:
- 将
i
的值增加 1-,使用++
运算符,这是i = i + 1
的快捷方式。 - 如果
i
< 10:- 将段落的值设置为
i
的当前值。 - 再次调用
toggleBox()
,传入i
。
- 将段落的值设置为
这样,你可以刷新你的页面,看到一个在停止前会淡入淡出五次的框。您还会看到显示发生次数的段落。
但是坚持住。为什么它显示 9,而不是 10?事实上,它已经淡入淡出十次了。原因是i
最初被设置为 0,所以盒子第一次褪色时,实际上是第零次褪色。因此,当i
为 9 时,它实际上发生了十次。
通常的做法是让count
变量从 0 开始,主要是因为数组是零索引的,正如你在第一章中看到的。但是,您可能希望输出的值从 1 到 10,这很容易通过更改两行代码来实现
para.text(i);
到
para.text(i+1);
因此,1–10 将显示在浏览器中,但在幕后,它使用 0–9。
摘要
哇哦。这是艰难的一章,你已经做了很多:
- 看到了如何下载最新版本的 jQuery。
- 发现了什么是精简代码,以及为什么您应该总是使用精简的 jQuery 版本。
- 通过
fadeIn()
、fadeOut()
、fadeToggle()
介绍了一些动画。 - 使用回调函数在动画完成后运行一段代码。
- 通过
text()
方法更新了 DOM 中的文本。 - 发现了如何通过使用
$(document).ready()
使代码仅在 DOM 加载后运行。 - 使用 jQuery API 找到您想要的方法。
如果你感到有点害怕,不要担心。这是 jQuery 所能提供的一些功能的一个短暂停留。下一章通过展示如何遍历 DOM,更系统地介绍了 jQuery 所提供的一切。
三、穿越大教堂
您已经看到了 jQuery 如何工作,以及如何让动画框淡入淡出。现在是时候更有条理地看看这个库,探索它能做的一切了。本章没有涵盖 jQuery 必须提供的每一种方法,因为许多方法做的事情非常相似。也有一些方法做了完全相反的事情。例如,在第二章中,在看了fadeOut()
如何工作之后,你只简单地看了一下fadeIn()
,因为在见过fadeOut()
之后,很明显它会做什么。很多 jQuery 方法也有类似的情况。
然而,这一章不仅仅是所有 jQuery 遍历方法的文档。效率是这一章的一大部分——而且会被多次提到。本章的内容如下:
- 用 CSS 选择器选择元素并探索哪一个是最有效的。
- 使用 jQuery 伪选择器。
- 探索 jQuery 提供的各种遍历方法。
- 缓存选择器和链接方法以避免重选元素。
- 避免不必要的 DOM 工作。任何 jQuery 项目的瓶颈总是 DOM 交互。与 DOM 交互是昂贵的,所以你能做的次数越少越好。
jQuery 中的 CSS 选择器
jQuery 的魅力,以及它如此受欢迎的原因,当然与它如此易于使用这一事实有关。您可能熟悉 CSS,并且知道要通过 ID 选择元素,可以使用散列符号(#)。要按类选择元素,可以使用句点(。),等等。jQuery 允许您使用这些选择器(以及更多)从 DOM 中选择元素。同样重要的是,它提供了向后兼容性。所以即使你用的 CSS 选择器在 IE7 及以下不能用,在 jQuery 中用的时候还是可以用的。
然而,伴随着强大的能力而来的是巨大的责任,从计算的角度来看,很多这样的选择器是非常低效的。选择元素的最基本方法是按其 ID,如下所示:
$("#header");
$("#maincontent");
这种方式总是比通过类或标签名选择更快,其他常见的方式;例如:
$(".column");
$(".header");
$("body");
$("div");
通过 ID 选择是最佳方式的原因有两个。首先,JavaScript 有自己的按 ID 选择的机制—document.getElementById("header")
—所以当 jQuery 检测到您传入了一个 ID 时,它可以简单地调用那个方法。第二,应该只有一个元素具有特定的 ID,所以一旦找到结果,它就停止搜索。
Note
由您来确保一个页面上只有一个 ID 实例。如果有多个元素具有相同的 ID,JavaScript(以及 jQuery)将只返回第一个元素。一个 id 存在于多个元素上是无效的 HTML。
如果你通过一个类寻找某个东西,可能会有多个结果,所以 JavaScript 必须继续搜索整个 DOM。如果可以通过 ID 选择元素,那么就这样做。
另一件值得一提的事情是 jQuery 处理选择器结果的方式。无论返回一个元素还是五十个元素,结果都将返回一个类似数组的结构(它实际上不是一个数组,但很快会有更详细的描述)。假设你在一页上有一个段落,你运行$("p")
。看看你得到了什么:
[<p>Hey</p>]
如果你还有几个,你会得到这个:
[<p>Hey</p>, <p>Hey</p>, <p>Hey</p>, <p>Hey</p>]
Note
如果你使用的是谷歌浏览器以外的浏览器,你的输出可能会略有不同。前面的示例显示了 Chrome 浏览器开发工具的输出。例如,在 Firefox 中,输出是
[p, p, p]
请放心,代码会找到相同的元素集。只是控制台输出的方式不同而已。
这样做的好处之一是,您可以通过对结果使用.length
来轻松地找出返回的项目数,如下所示,因为结果的行为就像一个 JavaScript 数组:
$("p").length; // 4
在 jQuery 1.8 之前的版本中可以使用 jQuery 方法$("p").size()
,但是.size()
所做的只是返回使用.length
的结果,所以开发人员通常使用.length
。
在这个阶段,jQuery 可能看起来只是返回了一个常规数组,但事实并非如此。它返回一个 jQuery 对象。这个 jQuery 对象就像你在第一章 1 中探索的常规对象一样。它包含所有 jQuery 属性和方法,以及您执行的选择器中的元素。一种很好的理解方式是,jQuery 对象是一个增强的数组。在其核心,它有一个 DOM 元素列表——但远不止这些。请记住,当您运行$("p")
并返回看起来非常像数组的内容时,它并不是。它实际上是一个 jQuery 对象。
jQuery 的新用户发现最令人困惑的事情之一是,一些方法在它们返回的每个元素上都被调用,而另一些则没有。例如,假设你有一个包含四个段落的列表,你想给每个段落一个类。以下将起作用:
$("p").addClass("paragraph");
addClass()
方法非常简单明了。它只是向元素添加了一个类。注意,这个addClass()
方法在结果集中的每个元素上运行。还要注意的是,您不必循环遍历它们。如果您有一组元素并调用一个方法,jQuery 通常会隐式地为您执行循环。这真的很有用,但是可能有点混乱,所以只要记住 jQuery 会尽可能地为您循环。
当然,因为 jQuery 可以解析 CSS 选择器,所以可以给它传递非常复杂的选择器,比如:
$("div>ul a");
$("div#main p strong");
$("div.main p>li a");
但是这些选择器的缺点是它们越复杂,运行的时间就越长,代码执行的速度就越慢。jQuery 从右到左解析它的 CSS 选择器,所以最后一个例子做的是
- 定位所有定位元素。
- 过滤掉不在列表项中的锚元素。
- 过滤掉所有剩余的元素,这样剩下的元素就在一个段落的直接子元素
<li>
中。 - 仅选择类别
main
中的剩余元素。 - 仅选择那些在具有该类别
main
的div
内的剩余部分。
光是找到一些链接就要做很多工作。在决定使用哪个选择器时,您需要警惕并牢记这种事情。
遍历方法
遍历方法是让我们在 DOM 中“跳”来寻找特定元素的方法。遍历方法将帮助您通过多种方式从元素 A 到达元素 B。在本节中,您将研究这些方法是什么,并探索如何尽可能提高效率。jQuery 有大量的遍历方法,正如文档( http://api.jquery.com/category/traversing/
)将向您展示的那样。
在这一章的剩余部分,我们将讨论我们认为最有用的方法——也是你最常用的方法。一路上会有各种切线,进一步讨论你需要知道的东西。
您通常会有一组想要缩小范围的元素。也许你只想要第一个,或者最后一个,或者你想从数组中选择一个特定的。您可以使用eq()
方法来实现这一点。假设您的 HTML 包含一些段落,如下所示:
<p>Para 1</p>
<p>Para 2</p>
<p>Para 3</p>
那我们假设你跑了$("p")
。您将得到以下结果:
[<p>Para 1</p>, <p>Para 2</p>, <p>Para 3</p>]
eq()
方法将返回一个 jQuery 对象,其中包含特定索引处的元素。例如,$("p").eq(0)
将给出包含第一段的 jQuery 对象(记住,数组是零索引的)。假设您要运行以下内容:
alert($("p").eq(0).text());
您会看到Para 1
,因为获取第一个元素是如此常见的事情,以至于 jQuery 提供了first()
,它做的完全一样。得知还有一种last()
方法,你不会感到惊讶。
因为获得第一个结果是如此常见,jQuery 给了我们另一种做事方式。请考虑以下几点:
$("p:first");
$("p:eq(0)");
这两者会达到同样的效果。jQuery 支持许多这样的伪类。
Note
jQuery 支持大多数 CSS 伪类,但也有一些自己的伪类,比如:eq(0)
。你可以在 https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
找到更多关于伪类的信息
一些伪类直接匹配 CSS3 规范,但是其他的(包括前面两个)不匹配。在本章和本书的过程中,你会看到很多。
现在的问题是,当 jQuery 同时提供伪类选择器和方法时,应该使用哪一个。我们更喜欢使用方法而不是伪选择器。我们认为他们阅读能力更强,更容易明白发生了什么。这是因为当您使用该方法时,它不包含在选择器中。这意味着当您浏览代码时,对该方法的调用更加突出,因为它不在选择器中。
不过,还有另一个原因。更新、更现代的浏览器如 Google Chrome 和 Mozilla Firefox 支持两种非常强大的方法:querySelector()
和querySelectorAll()
。这些是强大的选择方法,可以解析任何 CSS 选择器。querySelector()
返回选择器的第一个匹配,querySelectorAll()
返回所有匹配。jQuery 将始终使用querySelector()
和querySelectorAll()
,如果它们可用的话,因为这样做可以更快地获得具有复杂选择器的元素。
如果使用$("p:first")
选择器,jQuery 不能简单地将"p:first"
传递给querySelectorAll()
方法,因为":first"
不是 CSS 伪类。但是,如果使用$("p").first()
,jQuery 可以将"p"
选择器传递给一个本地 JavaScript 方法——在本例中为getElementsByTagName()
——然后对$("p")
的结果调用first()
。任何本地方法总是最快的,所以无论何时您可以选择允许 jQuery 使用本地方法,您都应该这样做。
进一步遍历
一旦获得了初始的元素集,很有可能需要在其中进行进一步的搜索。以下面的 HTML 结构为例:
<div>
<p>Paragraph <strong>one</strong></p>
<p>Paragraph Two</p>
</div>
假设您首先选择了<div>
,然后将其保存到一个变量中(也称为缓存):
var myDiv = $("div");
现在让我们假设您想要查找那个<div>
中的所有段落。jQuery 提供了两种实现方式:
myDiv.find("p");
myDiv.children("p");
或者,当然,你本来可以写
$("div p");
综合考虑这三种方法,哪种最好?如果单从速度上来说,$("div p")
在新的浏览器中总是最快的——也就是说,那些支持querySelector()
和querySelectorAll()
的浏览器。如果你只为这些浏览器建立一个网站,那么在最初的选择中进行大部分过滤实际上更有效,所以你使用querySelectorAll()
。
如果你担心老版本和新版本的浏览器,那么$("div p")
通常是最慢的方法,特别是当你的选择器更复杂的时候。这样就剩下两种方法:find()
和children()
。这两种方法有一个重要的区别。API 将它们在 http://api.jquery.com/category/traversing/
children()
处描述为“获取匹配元素集合中每个元素的子元素,可选地由选择器过滤。”find()
被描述为“获取当前匹配元素集中每个元素的后代,由选择器、jQuery 对象或元素过滤。”
关键的区别在于每个描述的第三个词。第一个方法将获得每个元素的子元素,第二个方法获得子元素。看这张图表:
div
- p
- p
- - strong
- - - a
在这里,段落是div
的子段落。然而,段落、<strong>
和锚都是div
的后代。子元素只是直接的后代,而后代则意味着元素中的一切,不管它在哪个层次上。
在这种情况下,请检查以下结构:
<div>
<p>Paragraph <strong>one</strong></p>
<p>Paragraph Two</p>
</div>
你应该用children()
,而不是find
。原因是find()
将搜索 DOM 的每一层来尝试找到一个匹配,而children()
将只搜索元素的直接层来找到一个匹配。所以,当你只想要直系后代的时候,比如这个例子,children()
会更快。诚然,这是一个微小的速度差异,但它只会做你需要它做的事情——而find
会做得更多——所以坚持使用children()
是有意义的。这还表明您只选择了直系后代,使您的代码读起来更好。
有了children()
和find()
这两个方法,您就不局限于传入一个标签了。它们,以及所有类似的遍历方法,接受任何 CSS 选择器,就像您可能通过$()
传递给初始 jQuery 对象的那些一样,比如:
$("div").find("p>strong");
这将返回作为段落直接子元素的所有strong
元素,这些段落存在于一个div
中。
另一个非常有用的方法是siblings()
,如您所料,它获取当前元素的所有兄弟元素。以这个结构为例:
<div>
<p class="first-paragraph">Paragraph 1</p>
<p>Paragraph 2</p>
<p>Paragraph <strong>3</strong></p>
</div>
运行$("div").siblings()
不会给你任何结果。这是因为<div>
是该级别的唯一元素。要获得第一段的所有兄弟,您可以执行以下操作:
$(".first-paragraph").siblings();
这将给出包含其他两个段落的结果集,但不包含初始段落。如果您想将初始段落添加到元素集合中,这样您就拥有了元素的兄弟元素和原始元素,您可以使用.add()
,它可用于将其他元素添加到现有集合中。例如,考虑使用以下内容:
$(".main").add(".paragraphs");
它将留给您一个包含类"main"
的元素结果集,以及包含类"paragraphs"
的元素结果集。因此,在本例中,您可以执行以下操作:
$(".first-paragraph").siblings().add(".first-paragraph");
但是这不是很有效率。请注意,您运行了选择器两次。这意味着您要搜索 DOM 两次,这一点也不好。幸运的是,jQuery 提供了andSelf()
,这是一种更简单的方法。它从上一个选择中获取一组元素,并将其添加到当前选择中:
$(".first-paragraph").siblings().andSelf();
这给出了一个包括兄弟()
和初始段落的集合。andSelf()
你会发现自己并不经常使用这种方法,但了解这种方法非常有用。使用 DOM 结构,实际上还有另一种方法可以获得第一段的兄弟段落:
$(".first-paragraph").nextAll();
nextAll()
获取当前元素之后的所有兄弟元素。因此,对于这个 HTML,如下所示:
<div>
<p>Paragraph 1</p>
<p class="second">Paragraph 2</p>
<p>Paragraph 3</p>
</div>
运行$(".second").nextAll();
只会返回一个元素——第三段。运行$(".second").siblings()
给出两个元素——第一段和最后一段。因此nextAll()
获得 DOM 结构中当前元素之后的所有兄弟元素。还有一个相反的方法,prevAll()
,它获取当前元素之前的所有兄弟元素。还有prev()
和next()
,它们获取当前元素旁边的兄弟元素——在prev()
的情况下是当前元素之前的兄弟元素,或者在next()
的情况下是当前元素之后的兄弟元素。
链接方法
您可能已经注意到,前面的例子将两个方法链接在一起,就像这样
$(".first-paragraph").siblings().andSelf();
这是 jQuery 的关键特性之一。方法可以被一个接一个地调用,并被链接在一起。任何返回 jQuery 对象的方法都可以被链接。要判断一个方法是否返回 jQuery 对象,请查看其 jQuery API 文档的右上角(参见图 3-1 )。
img/A310335_2_En_3_Fig1_HTML.jpg)
图 3-1。
The top-right corner of the documentation shows that this method returns “jQuery”, meaning it can be chained
截图右上角显示该方法返回 jQuery。这意味着该方法可以被链接。
有些方法可以被链接,也可以不被链接,这取决于它们的使用方式。一种这样的方法是你在第 2 ,text()
章看到的。如果不带参数调用text()
,它将返回元素的文本。但是,如果您向它传递一些文本,它将设置该元素的文本,然后返回一个 jQuery 对象。text()
的文档显示了这一点。有两个不同的条目。
第一个是针对text()
本身,它返回文本。你可以看到文档表明它返回一个字符串(见图 3-2 )。
img/A310335_2_En_3_Fig2_HTML.jpg)
图 3-2。
This method cannot be chained because it returns a string
然后是text()
,它接受一个参数并设置文本。它确实返回了 jQuery 对象(参见图 3-3 )。
img/A310335_2_En_3_Fig3_HTML.jpg)
图 3-3。
When you use .text() to set the text, it returns jQuery , so it can be chained
一般的经验法则是,任何不显式返回除一组元素之外的东西的方法都可以被链接。
链接是避免多次选择元素的好方法,如下所示:
$("div").fadeOut();
$("div").css("color", "red");
$("div").text("hello world");
您可以这样做,而不是那样做并运行$("div")
三次:
$("div").fadeOut().css("color", "red").text("hello world");
空白在这里并不重要,所以如果你想把它们分成多行,请随意。我们经常这样做:
$("div")
.fadeOut()
.css("color", "red")
.text("hello world");
注意不要遗漏方法之间的任何点,记住分号只在最后。如果您不喜欢链接,您可能更喜欢缓存选择,正如您已经看到的:
var div = $("div");
div.fadeOut();
div.css("color", "red");
div.text("hello world");
到目前为止,您已经使用了children()
和find()
来进一步遍历 DOM 结构,但是当然也有一些函数可以做完全相反的事情。这些被称为parent()
和parents()
。两者的关键区别在于parent()
只在 DOM 中向上一级,而parents()
一直向上。您可以在 jQuery 站点上找到这些函数的定义。
从字面上得到一个元素的所有父元素,直到最顶层的元素。以下面的 HTML 结构为例:
<div> <p><strong>Hello</strong></p> </div>
$("strong").parents()
的结果是
[<p>...</p>, <div>...</div>, <body>...</body>,<html>...</html>]
$("strong").parent()
的结果是
[<p>...</p>]
因为parents()
遍历整个 DOM,所以您几乎总是想要传递给它一些选择器,只是因为对于.parents()
来说,返回body
和html
元素很少有用。然而,有时您可能希望所有的父元素都包含在body
元素中,所以您经常需要做的是过滤掉parents()
返回的元素集。有两种方法可以做到这一点。第一种是使用 jQuery 过滤器。之前,您使用了:eq
选择器和.eq()
方法将结果过滤为一个结果。那是 jQuery 的过滤方法之一,API ( http://api.jquery.com/category/traversing/filtering/
)里都有记载。
我们现在感兴趣的方法是not()
。我们也将讨论其余的部分——一些在本章,一些在本书的其他地方。not()
完全按照您的预期进行操作——过滤结果。如果您想从您的parents()
通话中删除body
和html
元素,就像这样简单:
$("strong").parents().not("html, body");
这将获取所有的父标签并过滤掉html
和body
标签。这是因为not()
采用了 CSS 选择器。您实际上是告诉 jQuery 过滤掉与 CSS 选择器"html, body"
匹配的元素。当然,这个选择器匹配html
和body
元素。您也可以使用伪类:not
,就像您可以使用:eq
一样,但是如前所述,使用该方法比伪类更可取(并且更容易阅读),所以这是您将在本书的其余部分看到的。
然而,有一种更好的方法来做你想做的事情,那就是使用parentsUntil()
。有了parentsUntil()
,你的代码就简单多了。记住,parentsUntil()
获取所有元素,但不包括选择器匹配的元素。现在你已经知道了这个方法,你要做的就是下面的事情:
$("strong").parentsUntil("body");
这给出了期望的结果。jQuery 是一个强大的趋势。如果有些事情看起来有点罗嗦,很有可能有更简单的方法来做。
两个非常有用的过滤器是:even
和:odd
过滤器。将它们与采用过滤器并返回通过的过滤器的filter()
方法结合起来,您可以轻松地将背景颜色应用于行,使表格看起来有条纹。这里有一个简单的表格可以使用:
<!DOCTYPE html>
<html>
<head>
<title>Chapter 03, Exercise 01</title>
<script src="jquery.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<table>
<tr><td>Jack</td><td>Franklin</td></tr>
<tr><td>Stuart</td><td>Robson</td></tr>
<tr><td>Rob</td><td>Hawkes</td></tr>
<tr><td>Alex</td><td>Older</td></tr>
</table>
</body>
</html>
下面只是给出了表格的一些边框,以便清楚地定义行:
table {
border-collapse: collapse;
}
tr {
border-left: 1px solid grey;
border-right: 1px solid grey;
border-bottom: 1px solid grey;
}
td {
padding: 10px;
}
你可以在图 3-4 中看到结果。
img/A310335_2_En_3_Fig4_HTML.jpg)
图 3-4。
The plain table ready to apply the striped effect
下面是app.js
文件:
$(function() {
var rows = $("tr");
rows.filter(":even").css("background", "red");
rows.filter(":odd").css("background", "blue");
});
这给出了如图 3-5 所示的结果(当然不会赢得任何设计奖项)。
img/A310335_2_En_3_Fig5_HTML.jpg)
图 3-5。
The table once the code has run
app.js
文件做三件非常简单的事情:
- 将
$("tr")
的结果存储到变量rows
中。 - 过滤偶数行并将其涂成红色。
- 过滤奇数行并将其涂成蓝色。
这是css()
方法,但是非常简单。当传递两个参数(一个属性和一个值)时,它将设置集合中元素的 CSS 值。这个非常简单的例子向您展示了 jQuery 过滤器的强大功能。
进一步过滤
如果 jQuery 的内置过滤器还不够,它还提供了一种机制,允许您根据需要进行过滤。当您使用filter("even")
过滤偶数行时,您简要地看到了filter()
方法的作用。您还可以给filter()
传递一个函数,该函数将评估集合中的每个元素,并只返回符合特定条件的元素。
在你的index.html
页面中,添加四个段落,看起来像这样:
<!DOCTYPE html>
<html>
<head>
<title>Chapter 03, Exercise 02</title>
<script src="jquery.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<p><strong>Jack</strong> Franklin</p>
<p><strong>John</strong> Hammelink</p>
<p><strong>Richard</strong> Quick</p>
<p>Will Hammil</p>
</body>
</html>
摆脱你之前的一切;你不需要任何造型。
现在,让我们说,你想过滤只有段落有一个<strong>
标签,并给他们一个红色背景。你要做的第一件事是获取所有的段落,并把它们存储在一个变量中,就像这样:
$(function() {
var ps = $("p");
});
当你传递给filter()
一个函数时,它期望这个函数为每个元素返回true
或false. filter()
运行一次,当你传递的函数计算结果为true
时,它将保留元素。它将去掉使函数评估为false
的元素。
在这个函数中,你可以通过第一章中提到的this
关键字访问当前元素。this
关键字是 JavaScript 中的一个特殊变量,您可以经常使用它来引用您正在处理的当前项目。要访问您正在处理的但是包装在 jQuery 对象中的当前元素,您可以简单地运行$(this)
。
要过滤掉所有没有strong
元素的元素,您需要检查段落是否包含任何元素。有两条信息可以得到这个结果:
- 你可以通过
$("p").children("strong");
得到一个元素内的所有strong
元素 - 通过添加
.length
可以看到结果集中有多少个元素,比如:$("p").children("strong").length;
因此,对于包含strong
元素的段落,必须满足以下条件:
$("p").children("strong").length > 0;
这个表达式可以返回true
或false
,它就是你要传递给过滤函数的内容,就像这样:
$(function() {
var ps = $("p");
var strongPs = ps.filter(function() {
return $(this).children("strong").length > 0;
});
strongPs.css("background", "red");
});
你得到了想要的结果,如图 3-6 所示。
img/A310335_2_En_3_Fig6_HTML.jpg)
图 3-6。
Three of the four paragraphs, the ones with a inside, are given a red background
我相信你可以想象到,当传递一个函数时,filter()
方法是非常强大的。你可以过滤任何你想要的东西,只要你能把它评估为true
。
有一种方法可以简化代码。filter()
方法仍然返回 jQuery 对象,这意味着它可以被链接。这意味着您可以稍微缩短代码,如下所示:
$(function() {
var ps = $("p");
ps.filter(function() {
return $(this).children("strong").length > 0;
}).css("background", "red");
});
这里你使用了一个ps
变量,但是只引用了一次;把它扔掉,这样你就剩下了:
$(function() {
$("p").filter(function() {
return $(this).children("strong").length > 0;
}).css("background", "red");
});
好多了!
摘要
这是迄今为止最紧张的一章,你涵盖了许多新的领域。书中涉及的所有方法都会用到,所以如果有什么你不太确定的,不要担心——会有很多机会用到这些方法。对于 jQuery,很多都是实践,所以我们建议您自己编写一些代码。试试看。记得使用 API 文档——它真的很棒。在下一章,你将开始用 jQuery 操作 DOM。
四、使用 jQuery 操作 DOM
现在,您已经对 jQuery 及其功能有了一定的了解。您知道如何选择元素,如何确保您的代码只在 DOM 加载后运行,以及更多。您还看了一些动画,并通过使用css()
方法改变元素的颜色做了一些基本的操作。你可能没有意识到动画是一种操作。之前,您使用fadeIn()
/ fadeOut()
来操作一段时间内元素的不透明度。本章将完全集中在元素的操作上,包括:
- 用
css()
方法改变 CSS 样式 - 遇到 jQuery 的
animate()
方法时会有更多的动画 - 在 DOM 中插入、移除和移动元素
- 用
attr()
编辑元素属性 - jQuery 提供了无数的操作方法
在您的过程中,您将会有规律的停顿和小的偏离来检查最佳实践。正如在第三章中提到的,DOM 操作通常是网站的一个巨大瓶颈,所以你应该尽量少做。有很多技巧和方法可以限制花在 DOM 上的时间,我们会在你阅读本章的时候提到这些技巧和方法。
第三章还指出,jQuery API 让你很好地掌握了 API 的操作方法( http://api.jquery.com/category/manipulation/
),所以你可以随时参考。
半铸钢ˌ钢性铸铁(Cast Semi-Steel)
jQuery 的css()
方法非常强大。实际上有三种主要的方法可以使用它。第一种是在确定元素的属性值时。只需给它传递一个参数—您想要知道其值的属性:
$("div").css("width");
$("div").css("margin-right");
$("div").css("color");
重要的是要注意,如果你有一组不止一个的元素,并且你调用了css()
,你将得到的结果就好像是在第一个元素上调用了css()
。另一个重要的注意事项是,你不能使用速记。例如,这是行不通的:
$("div").css("margin");
Note
如果您使用css()
来获取宽度,那么您可能想看看 jQuery 的width()
、innerWidth()
和outerWidth()
方法。虽然css("width")
将返回一个类似"200px"
的字符串,但是宽度方法总是返回一个整数值。如果您正在执行任何基于宽度的计算,那么从一开始就将其作为一个整数要比获取一个字符串并转换它容易得多。
也可以使用 CSS 来设置值。若要只设置一个值,请将属性和值作为单独的参数传入。你在第三章用过这个。
$("div").css("color", "red");
$("div").css("border", "1px solid red");
更有用的是,css()
方法还接受一个键值对对象,该对象将 CSS 属性映射到您想要设置的值。例如:
$("div").css({
"background" : "red",
"margin-left": "200px",
"color": "black"
});
这是设置 CSS 属性更快的方法。然而,如果您发现自己经常这样做,很可能您应该实际创建一个新的 CSS 类来拥有这些属性,然后简单地用 jQuery 将该类添加到元素中。这意味着 jQuery 做的操作更少,因为它只需要添加一个类。要添加一个类,只需使用addClass()
方法:
$("div").addClass("column");
还有removeClass()
:
$("div").removeClass("column");
如果你想检查一个元素是否有一个特定的类,有hasClass()
:
$("div").hasClass("column");
这将返回true
或false
。如果你想给某个东西添加一个类,不管这个元素是否已经有了这个类,你都可以这样做。jQuery 足够聪明,可以为您解决所有这些问题。没有必要这样做:
if( !$("div").hasClass("main") ) {
$("div").addClass("main");
};
简单的叫addClass()
。类似地,在移除类之前,不需要检查元素是否有类。这两种方法都可以接受多个参数:
$("div").addClass("one two three");
$("div").removeClass("four five six");
如果元素没有类,您想添加一个类,但是如果元素有了类,您又想删除同一个类,那么 jQuery 也能满足您的需求:
$("div").toggleClass("main");
如果该集合中的元素具有该类,它们将移除该类;但是如果他们没有,它将被添加。
还有一些事情您可以使用css()
方法,因为 jQuery 提供了更好的替代方法。例如,要隐藏某个元素,可以将其 CSS“显示”属性更改为“无”:
$("div").css("display", "none");
然后你可以再展示一次:
$("div").css("display", "block");
但是,如果在您隐藏它之前,它的“显示”属性被设置为“内联”或“内联块”呢?jQuery 通过提供两个方法来解决这个问题:hide()
和show()
。它们的伟大之处在于,当您使用hide()
隐藏一个元素时,jQuery 不仅隐藏了它,还记住了它的“显示”属性。然后,当您在该元素上调用show()
时,它会将 display 属性设置回之前的状态。因此,要显示和隐藏元素,请按如下方式操作,而不要使用css()
方法:
$("div").hide();
$("div").show();
animate()和动画便利方法
你已经发现了动画,但是直到现在,你还没有遇到 jQuery 使用的主要动画函数:animate()
。从它的文档来看( http://api.jquery.com/animate/
),你会认为它非常复杂和难以使用,但实际上,它非常棒。即使你还没遇到过这种方法,但你目前为止用过的动画方法,包括fadeIn()
和fadeOut()
,都用的是animate()
。jQuery 提供了这些被称为便利方法的方法,以节省您的输入。下面是 jQuery 源代码中实现fadeIn()
的代码:
function (speed, easing, callback) {
return this.animate(props, speed, easing, callback);
}
它所做的只是传递您传递给animate()
方法的参数。如果没有 fade 方法,下面是淡出元素的方法:
$("div").animate({
"opacity": 0
}, 1000);
这将使div
的不透明度在 1000 毫秒或 1 秒内下降到 0。每次输入都会令人沮丧,所以实现了像fadeIn()
这样的便利方法来节省您的输入。还有很多更方便的方法,不只是针对动画,一般的也可以。你会在整本书中遇到很多。
animate()
的一般用法与css()
方法非常相似:它需要一个对象的属性和值来设置。第二个参数是制作属性动画所需的时间。第三个是回调函数,它的工作方式与您在本书前面传递给 fade 方法的函数完全一样。下面的代码片段向animate()
传递了三个参数。第一个是键-值对的对象,包含属性和您希望它们最终成为的值;第二个是以毫秒为单位的时间(还是那句话,1 秒= 1000 毫秒);第三个是回调函数。该功能将在动画完成后立即执行。
$("div").animate({
'width' : 200
}, 2000, function() {
alert("Div is now 200px wide!");
});
另一种常见的动画是动画显示元素的高度,通常通过“向上”滑动它们来隐藏它们,这样它们的高度为 0,实际上是隐藏的,或者通过“向下”滑动它们来显示高度,从而向用户显示元素。通过将高度设置为 0,您可以有效地隐藏div
:
$("div").animate({
'height' : 0
}, 2000);
但是由于这种情况非常普遍,jQuery 提供了三种方法来使事情变得更简单:
slideUp()
slideDown()
slideToggle()
这些方法通过高度来激活元素。将一个元素的高度设置为 0,创建一个元素在页面上向上滑动的效果,其高度越来越小,直到消失。slideDown()
相反,动画元素的高度到一个特定的值。最后,slideToggle()
根据调用元素时的状态,向上或向下滑动元素。如果在高度为 0 的元素上调用slideToggle()
,它会向下滑动并显示它。如果你在一个可见的元素上调用slideToggle()
,它会向上滑动。
现在看一个例子,看看如何使用这些方法。您将使用一小段 CSS 在页面上创建一个“盒子”,然后看看 slide 方法如何影响这个元素。
创建一个新文件夹来存放这些文件,并创建index.html
、app.js
和style.css
。添加您在之前所有练习中使用的基本 HTML(我们建议您简单地复制并粘贴一个旧练习,然后将其重命名)。你的index.html
应该是这样的:
<!DOCTYPE html>
<html>
<head>
<title>Chapter 04, Exercise 01</title>
<script src="jquery.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div id="box">
<p>A box</p>
</div>
</body>
</html>
迅速做出一些造型:
#box {
width: 200px;
height: 200px;
background: red;
}
让你的app.js
空白,准备好被一些令人惊叹的动画填充:
$(function() {
});
现在,选择div
后,添加对slideUp()
的调用:
$(function() {
$("#box").slideUp(2000);
});
刷新页面,您将看到框在 2 秒钟内滑出视图。记住,slideUp()
所做的只是调用animate()
方法;这是一条很好的捷径。
现在更改您的app.js
文件,使其看起来像这样:
$(function() {
var i = 0;
while( i < 10 ) {
$("#box").slideToggle(2000);
i++;
};
});
当你刷新页面时,你会看到框向上、向下、向上滑动,等等。它会滑动十次。前面的代码包含一个在变量i
小于 10 时运行的循环。在i
初始设置为 0 的情况下,这确保了循环将运行十次。在这个循环中,您调用slideToggle()
,如果它可见,它将向上滑动一个框,如果不可见,它将向下滑动一个框。下面的一行是i++
,它将i
的值增加 1。
属性和特性
为了获取和设置 DOM 元素的属性,jQuery 提供了attr()
方法。这就像css()
方法一样。做事有三种方式:
-
$("div").attr("id")
获取 ID 属性的值 -
$("div").attr("id", "main")
将 ID 属性的值设置为“main” -
$("div").attr({ "id" : "main", "rel" : "one"
});
一次设置多个属性
但是还有prop()
,它处理属性。attr()
处理属性。例如,给定一段 HTML,如下所示:
<input type="checkbox" checked="checked" />
那个checked
属性表示当页面加载时,复选框是否应该被选中。当用户与它交互时,它不会自我更新。这是通过checked
属性来管理的,该属性在用户勾选或取消勾选复选框时更新。这是一个你应该使用prop()
而不是attr()
的例子。属性是你可以在 HTML 中看到的,而属性是在“幕后”使用的如果你想阅读更多这方面的内容,jQuery 在 http://blog.jquery.com/2011/05/12/jquery-1-6-1-released/
的博客是一个很好的起点。它对不同之处以及何时应该使用其中一种进行了详尽的解释。
根据我们的经验,绝大多数时候,您可以(也应该)使用attr()
。甚至当你在更倾向于使用prop()
的地方使用attr()
时,attr()
也会简单地为你调用prop()
。这并不意味着你应该简单地总是使用attr()
——使用prop()
可以提高速度。在本书中,你会看到应该使用prop()
的时候,特别是在本书的后面,当你看 HTML 表单的时候。
当在窗口或文档对象上设置属性时,你应该总是使用prop()
,仅仅因为窗口和文档对象不是 HTML 元素——因为它们没有属性。
这里prop()
的用法和attr()
完全一样,不用演示怎么用了。这一部分的关键是它们之间的区别。这可能是一个令人困惑的差异,需要一段时间才能理解。如果您需要重读这一部分,请不要担心。
文本()和 html()
如果想要更新元素中的一些文本,最好的方法是使用text()
方法。像许多其他 jQuery 方法一样,它将在不带参数调用时返回文本的值;但是当用一个参数调用时,它将设置一个元素的文本。例如:
<p>Hello</p>
$("p").text(); //Hello
$("p").text("Hey");
$("p").text(); //Hey
$("p").text("<strong>Hey</strong");
$("p").text(); //<strong>Hey</strong>
当您将 HTML 传递给text()
方法时,它会自动为您进行转义。这意味着 jQuery 替换了符号““<
”。然后,浏览器将此显示为“收件人”>
。这意味着您将看到文本“嘿”,而不是包裹在strong
标签中的单词“嘿”。
strong
标签被转义,所以它没有被应用。这就是html()
的用武之地。html()
的工作方式与text()
完全相同,但不会对其中的任何 HTML 进行转义。这意味着在前面的例子中,如果你使用html()
而不是text()
,你会看到它被解释为 HTML,这意味着strong
标签没有被转义,因此你会看到单词“嘿”,现在应该是粗体的。
但是,您不应该通过这些方法插入复杂的 HTML。jQuery 提供了无数插入 DOM 的选项,您很快就会看到。
从 DOM 中移除元素
jQuery 有很多从 DOM 中移除元素的方法,你可以在 http://api.jquery.com/category/manipulation/dom-removal/
找到。
先说看起来最明显的一个:remove()
。它从 DOM 中删除了元素集,但也删除了与之相关的任何东西——比如事件。假设您有一些在元素被单击时运行的代码,但是您随后移除该元素并将其重新插入到 DOM 的其他地方。在这种情况下,您必须重新分配该代码,以便在单击该元素时运行。运行remove()
完全摆脱了与之相关的任何东西。
使用remove()
返回匹配选择器的整个元素集,而不仅仅是它刚刚移除的元素集。这是一个容易犯的错误。给定这个 HTML 结构:
<div>
<p>hey</p>
</div>
这个 JavaScript:
$(function() {
var p = $("p").remove();
console.log(p);
});
记录了以下内容。请注意,这些示例是从 Google Chrome 开发者控制台生成的。如果您使用不同的,您可能会得到不同的输出。例如,对于下一个例子,Firefox 控制台将简单地显示[p]
。不要担心这个——这只是每个浏览器如何将结果输出到它的控制台。
[<p>Hey</p>]
这会让你认为它会把它拿走的东西还给你。但是,如果您有这个 HTML:
<div>
<p>hey</p>
<p class="remove">hello</p>
</div>
这个 JavaScript:
$(function() {
var p = $("p").remove(".remove");
console.log(p);
});
你把这两段都记录下来:
[<p>hey</p>, <p class="remove">hello</p>]
请注意您是如何将选择器传递给remove()
方法来过滤结果的。这里我们只从选择中选择了类别为“remove”的元素,$("p")
。在这种情况下,我们本可以使用$(".remove").remove()
取得同样的成绩。
您可能会问,如果您无法获得刚刚移除的内容,如何将该元素重新插入 DOM,以便将它从 DOM 中的一个位置移动到另一个位置。我们稍后将讨论如何将该元素添加回 DOM。
如果您想从 DOM 中移除一个元素,但不想移除它的关联,那么可以使用detach()
,它的工作方式与remove()
完全相同,除了它不会移除当元素被点击时运行的代码之类的东西,尽管您已经将元素从 DOM 中移除了。因此,如果你要重新插入用detach()
移除的元素,任何事件处理程序仍然会触发。另一方面,remove()
完全删除元素和所有事件处理程序。
有时,您可能希望删除元素中的所有内容,而不是元素本身。这就是empty()
的用武之地。以之前使用的 HTML 结构为例:
<div>
<p>hey</p>
<p class="remove">hello</p>
</div>
运行这段 JavaScript 代码:
$(function() {
var d = $("div").empty();
console.log(d);
});
记录以下内容(同样,如果您在不同的浏览器中,您可能会得到略有不同的输出):
[<div></div>]
div
被完全清空。关于empty()
非常重要的一点是,它还会删除元素中的文本。这是因为,从技术上讲,文本是一个 DOM 节点,而empty()
从元素中移除所有节点。
移除元素的最后一个方法是unwrap()
,它的操作大致与empty(). empty()
相反,获取元素并移除其子元素,而unwrap()
获取元素并移除其父元素。鉴于以下情况:
<div>
<p>hey</p>
</div>
以及下面的 JavaScript:
$(function() {
$("p").unwrap();
console.log($("body").html());
});
一旦您调用了unwrap()
,您就可以使用html()
方法,当不带任何参数调用该方法时,它会返回一个字符串,该字符串是该元素中的 HTML。可以预见的结果是
[<p>Hey</p>]
简单地删除它所调用的元素的父元素。
创建新元素
在开始向 DOM 插入新内容之前,首先需要了解如何创建一个新对象。最简单的方法是创建一个 HTML 字符串。您将看到的大多数插入方法都会欣然接受这一点:
var newParagraph = "<p>Hello</p>";
然而,如果您插入更复杂的结构,这可能会变得非常复杂。像上一个例子一样,用一个字符串做快速加法没有错;但是对于任何更复杂的东西,你应该像这样创建它:
var newDiv = $("<div></div>", {
"text": "Hello",
"class": "newDiv",
"title": "Hello"
});
这是创建复杂元素的最佳方式,因为复杂元素需要设置很多属性。您在一个空元素上调用 jQuery,然后传入一个将属性映射到值的对象。注意,您也可以使用 jQuery 方法。前面的例子表示一个属性“text ”,并给它一个值“Hello ”, jQuery 随后转换这个值,并使用它的text()
方法将文本设置为“Hello”。然后,可以将这个新元素保存到一个变量中,这样就可以将它插入到 DOM 中。让我们现在就做吧!
插入到 DOM 中
你终于来了!这需要一段时间,但是您最终可以看到如何将东西放入 DOM。到目前为止,您已经操作了现有的元素并删除了它们,但是没有添加任何东西。jQuery 提供了大量向 DOM 中插入内容的方法,因此您将看到一些最流行的方法。我们还将讨论效率,因为当涉及到 DOM 时,低效地做事在计算上是非常昂贵的。如果您看一下 jQuery 文档,会发现有三类 DOM 插入方法:
- DOM Insertion,Around:这些方法允许您在现有元素周围插入元素。
- DOM Insertion,Inside:这些方法允许您在现有元素中插入元素。
- DOM Insertion,Outside:这些方法允许您在完全独立的现有元素之外插入元素。
我们将讨论每一个类别。有太多的方法来讨论它们,但是像往常一样,我们将选择我们最经常使用的方法和我们看到其他人经常使用的方法。
DOM 插入,周围
在这个部分中只有三种方法。您会记得您曾经看过unwrap()
以及它是如何被用来移除一个元素的父元素的。“around”方法都做了与此相反的事情:它们在现有元素周围包装新元素。有三种方法:
wrap()
wrapAll()
wrapInner()
您最常使用的是wrap()
,但是其他方法也很有用,所以您将从wrap()
开始研究它们。对于所有使用wrap()
的例子,您将使用这个 HTML 结构:
<div>
<p>hey</p>
</div>
最简单的用法是向wrap()
传递一个字符串:
$("p").wrap("<div></div>");
这给了你:
<div>
<div>
<p>Hey</p>
</div>
</div>
这在段落元素周围包装了一个新的div
元素。
你可以把它缩短一点。当传入一个新的空元素 HTML 字符串时,只需执行以下操作就可以节省一些字符:
$("p").wrap("<div />");
当然,您也可以通过创建新的元素来包装元素,方法是通过传入 HTML 字符串和一个 properties 对象来创建新元素。然后您可以调用wrap()
,传入您刚刚创建的新对象。例如:
var newDiv = $("<div />", {
"class" : "Hello",
"text": "Hey"
});
$("p").wrap(newDiv);
给你:
<div> <div class="Hello">Hey<p>hey</p></div> </div>
你可以看到你的段落已经用新的div
换行了。
做一些类似的事情:它获取集合中的每个元素,并将它们全部包装在新元素中。因此,在前面的例子中,您可以使用wrapAll()
来获得相同的效果。如果你有两个段落并调用wrapAll()
,它们都被包裹在一个div
中。例如:
<div>
<p>Hey</p>
<p>Hello</p>
</div>
$("p").wrapAll("<div />");
给你:
<div>
<div><p>Hey</p><p>Hello</p></div>
</div>
如果您需要在整个元素组周围添加新的包含元素,这将非常有用。
最后一个 wrap 函数是wrapInner()
,将每个元素的内容包装在新元素中。例如,前面运行的 HTML 结构
$("p").wrapInner("<strong />");
给你:
<div> <p><strong>Hey</strong></p> <p><strong>Hello</strong></p> </div>
DOM 插入,内部
DOM Insertion,Inside 方法允许您获取 DOM 中的现有元素,并在其中添加元素。jQuery 文档( http://api.jquery.com/category/manipulation/dom-insertion-inside/
)对此做了最好的总结:“这些方法允许我们在现有元素中插入新内容。”
这里有六种方法(您已经见过其中两种):
append()
appendTo()
html()
prepend()
prependTo()
text()
您已经看到并使用了html()
和text()
,所以我们将跳过这一部分。你大概可以猜到prepend()
和append()
(以及“To”版本)做的事情非常相似;这里没有太多要说的。你可能会发现自己经常使用append()
和prepend()
,所以理解这些例子很重要。
非常简单。你给它传递一个新元素,或者一些 HTML,它把它添加到集合中每个元素的末尾。最好用一个例子来解释,假设您有三个空的div
元素:
<div></div>
<div></div>
<div></div>
并运行:
var p = $("<p />", {
"text" : "Hello"
});
$("div").append(p)
因此,DOM 现在看起来像这样,每个div
包含一个新段落:
<div><p>Hello</p></div>
<div><p>Hello</p></div>
<div><p>Hello</p></div>
需要注意的是,append()
在元素的最后添加内容,在所有其他元素之后。从这里,你大概可以猜到prepend()
会在开头插入元素,在所有其他元素之前。取一个已经包含一些内容的div
,运行prepend()
会在该内容之前插入新元素,而append()
会将其放在该内容之后。以前面 HTML 中的这个div
为例:
<div><p>Hello</p></div>
如果您随后运行相同的 jQuery 代码片段,但是更改了段落中的文本:
var p = $("<p />", {
"text" : "Howdy"
});
$("div").append(p)
你会看到:
<div><p>Hello</p><p>Howdy</p></div>
但是如果你跑了:
var p = $("<p />", {
"text" : "Howdy"
});
$("div").prepend(p)
你会看到:
<div><p>Howdy</p><p>Hello</p></div>
prependTo()
和appendTo()
只是一种不同的代码编写方式。使用append()
,您选择元素,然后向其添加内容,而appendTo()
执行相反的操作。例如:
<p>Howdy</p>
$("<strong>hello</strong>").appendTo("p")
给你:
<p>Howdy<strong>hello</strong></p>
appendTo()
接受一个参数,它可以是任何 CSS 选择器或 jQuery 对象。因此,您可以使用以下代码获得相同的结果:
var p = $("p");
$("<strong>Hello</strong>").appendTo(p);
这些在prependTo
中也能很好地工作(这两种方法在这方面完全相同)。例如:
var p = $("p");
$("<strong>Hello</strong>").prependTo(p);
结果如下:
<p><strong>Hello</strong>Howdy</p>
DOM 插入,外部
这些方法允许您在其他元素之外插入内容。这里有四种方法,但实际上只有两种,因为它们是完全相反的,就像prepend()
和append()
:
after()
before()
insertAfter()
insertBefore()
after()
用于在你的集合中的元素后插入内容。例如:
<div><p>Hello</p></div>
$("p").after("<span>Hey</span>");
会给你:
<div><p>Hello</p><span>Hey</span></div>
如果你要做:
$("p").before("<span>Hey</span>");
你会得到:
<div><span>Hey</span><p>Hello</p></div>
从这里开始,根据您在上一节中所做的,您大概可以猜出insertAfter()
是做什么的。这只是一种不同的语法:
$("<span>Hey</span>").insertAfter("p");
$("<span>Hey</span>").insertBefore("p");
我们倾向于使用这种语法— prependTo()
、insertAfter()
等等—比其他语法多得多。这主要是因为使用一种方法,其中您想要插入的东西是代码中的“第一个”,您可以创建新元素并更容易地插入它们。例如,这个:
$("<p />", {
"text": "Hello"
}).insertAfter("p");
比这个好得多:
var p = $("<p />", {
"text": "Hello"
})
$("p").after(p);
在本书的后续练习中,我们将倾向于使用insertAfter()
、appendTo()
等,但是如果您喜欢这样做,可以随意交换它们。
有效的 DOM 插入
在继续第五章中的事件之前,我们想花点时间讨论一下效率。正如我们已经提到的,DOM 操作是昂贵的。相对于您将要编写的大多数 JavaScript,移除、操作或插入 DOM 元素将是最慢的部分。我们看到人们低效使用它的最常见的例子是当他们在一个循环中插入大量内容时。假设您想要生成一个由 1 到 10 的数字组成的无序列表,并且您决定在一个循环中执行这个操作是一个很好的方法。你可以这样做。为这个练习创建一个新文件夹,并设置通常的结构,一个index.html
文件和一个app.js
文件,其中只包含
$(function() {
});
喜欢的话可以加个style.css
做造型。以下是列出清单的初步尝试:
$(function() {
var i = 0;
var newUl = $("<ul />").appendTo("body");
while( i < 10 ) {
$("<li />", {
"text" : i+1
}).appendTo(newUl);
i++;
}
});
以下是步骤:
- 创建一个新的无序列表,并将其添加到
<body>
元素中。 - 然后循环十次。在循环中,创建一个新的列表项,并将其添加到无序列表中。
- 将文本值设置为 i+1,因此列表项显示为 1–10,而不是 0–9。
您可能认为这很好,但是在这段代码中,您将插入 DOM 次——一次插入列表,一次插入每个列表项。那是一笔巨款。如果你制作无序列表会更好,但是不要把它插入到 DOM 中。然后将每个列表项添加到这个无序列表中,并将整个列表项插入到 DOM 中,将 DOM 插入的数量从 11 个减少到 1 个。这很容易做到:
$(function() {
var i = 0;
var newUl = $("<ul />");
while( i < 10 ) {
$("<li />", {
"text" : i+1
}).appendTo(newUl);
i++;
}
newUl.appendTo("body");
});
这里的关键是,在循环完成之前,不要将无序列表追加到主体中。在将元素添加到 DOM 之前,可以创建一个元素并向其中添加元素。那是做这件事的最好方法。您仍然将每个列表项追加到无序列表中,但是这不是一个 DOM 插入,因为这个无序列表没有被添加到 DOM 中。只是在最后才加上的。在写这篇文章的时候,我们很好奇这会带来多大的不同,我们发现在循环中插入比在末尾插入慢 45%到 60%。当然,在这种情况下,差异要小得多,因为您只插入了十个元素,但是您应该始终致力于生成高效的代码,并且注意像这样的情况是其中的一大部分。
摘要
在一章的篇幅中,涵盖了大量内容。现在,您可以操纵 DOM 了,与一章前相比,您可以做更多的事情。接下来,您将了解事件以及如何编写基于用户交互执行的代码。您还将构建一个手风琴,将事件和 DOM 操作放在一起。