4.3 效果和时长

当在.show().hide()中指定时长(或更准确地说,一个速度)参数时,就会产生动画效果,即效果会在一个特定的时间段内发生。
例如.hide('duration')方法,会同时减少元素的高度、宽度和不透明度,直至这3个属性的值都达到0,与此同时会为该元素应用CSS规则display:none
.show(' duration ')方法则会从上到下增大元素的高度,从左到右增大元素的宽度,同时从0到1增加元素的不透明度,直至其内容完全可见。

4.2 隐藏和显示元素

基本的.hide().show()方法不带任何参数。可以把它们想象成类似.css('display',’string')方法的简写方式,其中string是适当的显示值。不错,这两个方法的作用就是立即隐藏或显示匹配的元素集合,不带任何动画效果。
其中,.hide()方法会将匹配的元素集合的内联style属性设置为display:none。但它的聪明之处是,它能够在把display的值变成none之前,记住原先的display值,通常是blockinlineinline-block
恰好相反,
.show()方法会将匹配的元素集合的display属性,恢复为应用display: none之前的可见属性

.show().hide()的这种特性,使得它们非常适合隐藏那些默认的display属性在样式表中被修改的元素。例如,在默认情况下,<li>元素具有display:list-item属性。但是,为了构建水平的导航菜单,它们可能会被修改成display:inline。而在类似这样的<li>元素上面使用.show()方法,不会简单地把它重置为默认的display:list-item,因为那样会把<li>元素放到单独的一行中;相反,.show()方法会把它恢复为先前的display:inline状态,从而维持水平的菜单设计。
要示范这两个方法,最明显的例子就是在前面的HTML中再添加一个新段落,然后在第一个段落末尾加上一个read more链接:

DOM就绪时,选择一个元素并调用.hide()方法,参见代码清单4-6。

1
2
3
$(document).ready(function() { 
$('p').eq(1).hide();
});

这里的.eq()方法与第2章中讨论的:eq()伪类相似。这个方法返回jQuery对象,其中包含一个元素(索引从0开始)。在这个例子中,.eq()方法选择第二个段落并隐藏该段落,结果看起来如图4-3所示。

本文重点

隐藏元素

,**.hide()方法会将匹配的元素集合的内联style属性设置为display:none**。并且能够在把display的值变成none之前,记住原先的display值。

显示元素

.show()方法会将匹配的元素集合的display属性,恢复为hide()方法应用display: none之前的可见属性

eq方法

这里的.eq(x)方法与第2章中讨论的:eq()伪类相似。这个方法返回jQuery对象中的x+1个元素(从0开始),例如$('p').eq(1)表示取得第2个段落.

event.preventDefault方法

event.preventDefault方法可以避免该事件对象默认操作,如果event是链接的话,则event.preventDefault()表示阻止链接的默认操作.

4.1.2 带厂商前缀的样式属性

浏览器厂商在引入试验性的样式属性时,通常会在实现达到CSS规范要求之前,在属性名前面添加一个前缀。等到实现和规范都稳定之后,这些属性的前缀就会被去掉,让开发人员使用标准的名称。
因此,我们经常会在样式表里看到一些类似下面这样的CSS声明:

1
2
3
4
5
-webkit-property-name: value; 
-moz-property-name: value;
-ms-property-name: value;
-o-property-name: value;
property-name: value;

如果想在JavaScript中设置这些属性,需要提前检测它们在DOM中是否存在,从propertNameWebkitPropertyName,再到msPropertyName……都要检测。但jQuery中,我们可以直接使用标准的属性名,比如:.css('propertyName', 'value')。如果样式对象中不存在这个属性,jQuery就会依次检测所有带前缀(WebkitOMozms)的属性,然后使用第一个找到的那个属性。

本文重点

jQuery中,应该直接使用标准的属性名,不要使用带浏览器前缀的属性名.

4.1.1 设置计算的样式属性值

如果每次都增大或减小为预定的值,那么仍然可以使用.addClass()方法。但是,这次假设我们希望每单击一次按钮,文本的字体大小就会持续地递增或递减。虽然为每次单击定义一个单独的类,然后迭代这些类也是可能的,但更简单明了的方法是每次都以当前字体大小为基础,按照一个设定的系数(例如40%)来递增字体大小
同以前一样,我们的代码仍然是从$(document).ready()$('#switcher-large').click()事件处理程序开始,参见代码清单4-1。

1
2
3
4
$(document).ready(function() { 
$('#switcher-large').click(function() {
});
});

接着,通过$('div.speech').css('fontSize')可以轻而易举地取得当前的字体大小。不过,由于返回的值中包含数字值及其单位(px),需要去掉单位部分才能执行计算。同样,在需要多次使用某个jQuery对象时,最好也把这个对象保存到一个变量中,从而达到缓存数据的目的。为此,就需要引入两个变量,参见代码清单4-2。

1
2
3
4
5
6
$(document).ready(function() { 
var $speech = $('div.speech');
$('#switcher-large').click(function() {
var num = parseFloat($speech.css('fontSize'));
});
});

$(document).ready()中的第一行代码把<div class="speech">保存到一个变量中。注意变量名$speech中的$。由于$JavaScript变量中合法的字符,因此可以利用$来提醒自己该变量中保存着一个jQuery对象。与PHP等编程语言不同,$符号在jQuery或者说JavaScript中没有特殊的含义。
.click处理程序中,通过parseFloat()函数只取得字体大小属性中的数值部分parseFloat()函数会在一个字符串中从左到右地查找一个浮点(十进制)数。例如,它会将字符串’12’转换成数字12。另外,它还会去掉末尾的非数字字符,因此’12px'就变成了12。如果字符串本身以一个非数字开头,那么parseFloat()会返回NaN,即Not a Number(非数字)。
至此,所剩的就是修改解析后的数值并根据新值来重设字号大小了。在这个例子中,我们要在每次按钮被单击时把字号增大40%。为此,可以将num乘以1.4,然后再连接num和’px'来设置字体大小,参见代码清单4-3。

1
2
3
4
5
6
7
8
$(document).ready(function() { 
var $speech = $('div.speech');
$('#switcher-large').click(function() {
var num = parseFloat($speech.css('fontSize'));
num *= 1.4;
$speech.css('fontSize', num + 'px');
});
});

现在,当用户单击Bigger按钮时,文本会变大,再次单击,会继续变大,如图4-2所示。

要通过单击Smaller按钮减小字体大小,应该使用除法而不是乘法,即num /= 1.4。同样,更好的方案是把对这两个按钮的单击操作,通过<div id="switcher">中的<button>元素组合到一个.click()处理程序中。在查找到数值后,再根据用户单击的按钮ID来决定使用乘法还是除法,如代码清单4-4。

1
2
3
4
5
6
7
8
9
10
11
12
$(document).ready(function() { 
var $speech = $('div.speech');
$('#switcher button').click(function() {
var num = parseFloat($speech.css('fontSize'));
if (this.id== 'switcher-large') {
num *= 1.4;
} else if (this.id== 'switcher-small') {
num /= 1.4;
}
$speech.css('fontSize', num + 'px');
});
});

根据第3章学习的内容,我们知道可以访问this.id得到this引用的DOM元素的id属性,因而就有了ifelse if语句中的代码。这里,如果仅测试属性的值,使用this显然要比创建jQuery对象更有效。
如果提供一种方式能够返回字体大小的初始值当然更好了。为了做到这一点,可以在DOM就绪后立即把字体大小保存在一个变量中。然后,当用户单击Default按钮时,再使用这个变量的值。虽然可以通过再添加一个else if语句来处理这次单击,但此时改用switch语句应该更合适,参见代码清单4-5。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$(document).ready(function () {
// 获取要操作的DOM元素
var $speech = $('div.speech');
// 保存原始的字体大小
var defaultSize = $speech.css('fontSize');
// 监听按钮点击事件
$('#switcher button').click(function () {
// 获取字体大小
var num = parseFloat($speech.css('fontSize'));
// 判断当前按钮的id
switch (this.id) {
case 'switcher-large':
num *= 1.4;
break;
case 'switcher-small':
num /= 1.4;
break;
default:
num = parseFloat(defaultSize);
}
// 重新设置字体的大小
$speech.css('fontSize', num + 'px');
});
});

在此,仍然是检查this.id的值并据以改变字体大小,但如果它的值既不是'switcher-large'也不是'switcher-small',那么就应该使用默认的初始字体大小。

总结

.addClass()方法不适用于动态设置样式

.addClass()方法适用于设置样式为固定值的情况。
如果希望样式持续的增大或减小,则要定义多个类,然后再迭代这些类来实现持续变大或变小的效果.这种方式麻烦。

如何获取DOM元素中字体的大小

$('选择符').css('fontSize')可以轻而易举地取得该DOM中的字体大小,这个方法的返回值包含数字单位

保存jQuery对象的变量名命名规则

可以在变量名中加入$符号来提醒自己该变量中保存着一个jQuery对象

parseFloat()函数

parseFloat()函数的功能

parseFloat()函数可以取得字符串中的数字部分,例如:通过parseFloat()函数只取得字体大小属性中的数值部分
parseFloat()函数会在一个字符串中从左到右地查找一个浮点(十进制)数。例如,

  • 它会将字符串'12'转换成数字12。
  • 另外,它还会去掉末尾的非数字字符,因此'12px'就变成了12
  • 如果字符串本身以一个非数字开头,那么parseFloat()会返回NaN,即Not a Number(非数字)。

如何通过按钮来持续的增大或减小字体的大小

$(document).ready()方法的匿名函数中写入方法,算法描述如下

  • 1.获取要操作的DOM元素的jQurey对象
  • 2.保存该jQurey对象的fontSize样式属性的值
  • 3.监听按钮点击事件
    • 3.1获取jQurey对象的fontSize样式属性值中的数值部分
    • 3.2根据触发点击事件DOM元素的id来判断具体触发的是哪个按钮
      • 如果是增大按钮,则使用乘法增大字体大小
      • 如果是减小按钮,则使用除法减小字体大小
      • 如果是默认按钮,则获取默认的fontSize属性值中的数值部分
  • 4.拼接数值和单位组成fontSize的属性值,然后通过jQury对象的css方法更新fontSize属性的属性值

3.7 小结

本章学习了各种响应用户及浏览器发起事件的方法,包括如何在页面加载时安全地执行代码、如何处理单击链接和悬停按钮时的鼠标事件,以及如何截获按键输入。
此外,我们介绍了事件系统的内部机制,并据以实现了事件委托和改变事件行为。我们甚至都可以模仿用户发起事件。
基于这些技术,可以构建极具交互性的页面。下一章,我们学习如何在这些交互中给用户提供反馈。

3.6 模仿用户操作

有时候,即使某个事件没有真正发生,但如果能执行绑定到该事件的代码将会很方便。例如,假设我们想让样式转换器在一开始时处于折叠状态。那么,可以通过样式表来隐藏按钮,或者在$(document).ready()处理程序中调用.hide()方法。不过,还有一种方法,就是模拟单击样式转换器,以触发我们设定的折叠机制。

.trigger()方法

通过.trigger()方法就可以完成模拟事件的操作,如代码清单3-23所示。

1
2
3
$(document).ready(function() { 
$('#switcher').trigger('click');
});

这样,随着页面加载完成,样式转换器也会被折叠起来,就好像是被单击了一样。
如果我们想向禁用JavaScript的用户隐藏一些内容,以实现优雅降级,那么这就是一种非常合适的方式。

.trigger()方法简写

.trigger()方法提供了一组与.on()方法相同的简写方法。当使用这些方法而不带参数时,结果将是触发操作而不是绑定行为,如代码清单3-24所示。

1
2
3
$(document).ready(function() { 
$('#switcher').click();
});

响应键盘事件

作为另一个例子,我们还可以向样式转换器中添加键盘快捷方式。当用户输入每种显示样式的第一个字母时,可以让页面像响应按钮被单击一样作出响应。要实现这种功能,需要先了解键盘事件``,键盘事件与鼠标事件稍有不同。

键盘事件

键盘事件可以分为两类:

  1. 直接对键盘按键给出响应的事件(keyupkeydown)
  2. 对文本输入给出响应的事件(keypress)。

两类键盘事件的用途

输入一个字母的事件可能会对应着几个按键,例如输入大写的X要同时按ShiftX键。虽然各种浏览器的具体实现有所不同,但有一条实践经验还是比较可靠的:如果想知道用户按了哪个键,应该侦听keyupkeydown事件;如果想知道用户输入的是什么字符,应该侦听keypress事件。对于这里想要实现的功能而言,我们只想知道用户什么时候按下了DNL键,因而就要使用keyup

键盘事件的目标

接下来,需要确定哪个元素应该侦听这个事件。相对于可以通过鼠标指针确定事件目标的鼠标事件而言,这个细节就没有那么明显了。事实上,键盘事件的目标是当前拥有键盘焦点的元素。元素的焦点可能会在几种情况下转移,包括单击鼠标和按下Tab键。

什么样的元素可以获得键盘焦点

并非所有元素都可以获得焦点,只有那些默认情况下具有键盘驱动行为的元素,如表单字段链接,以及指定了tabIndex属性的元素才可以获得焦点

应用事件冒泡

对于眼前的例子来说,哪个元素获得焦点其实并不重要,我们只想让转换器在用户按下某个键时能够有所反应。这一次,又可以利用事件冒泡了——因为可以假设所有键盘事件最终都会冒泡到document元素,所以可以把keyup事件直接绑定到该元素。

如何知道用户按的是哪个键

最后,需要在keyup处理程序被触发时知道用户按下了哪个键。此时可以检查相应的事件对象,事件对象的.which属性包含着被按下的那个键的标识符。对于字母键而言,这个标识符就是相应大写字母的ASCII
因此,可以为字母和相应的按钮创建一个对象字面量。在用户按下某个键时,可以查找它的标识符是否在这个对象里,如果在则触发单击事件,参见代码清单3-25。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$(document).ready(function () {
// 创建快捷键对象
var triggers = {
D: 'default',
N: 'narrow',
L: 'large'
};
// 注册匿名的键盘事件处理程序
$(document).keyup(function (event) {
// 获取按下的键的标识,返回的是键的ASCII码
var key = String.fromCharCode(event.which);
// 如果是快捷键
if (key in triggers) {
// 点击该快捷键对应的按钮
$('#switcher-' + triggers[key]).click();
}
});
});

这样,按下这三个键中的任何一个,都会模拟鼠标对相应按钮的单击——前提是键盘事件没有被某些特性(例如Firefox的”在输入时搜索文本”功能)所截取。
除了使用.trigger()模拟单击外,下面我们再深入一步,看一看怎样把相关代码提取到一个函数中,以便更多处理程序(clickkeyup)可以调用它。尽管在本例中没有必要这样做,但这种技术确实有利于消除冗余代码,参见代码清单3-26。

最后这次修改整合了本章前面所有的代码示例。我们把整块代码都挪到了$(document).ready()处理程序中,代码看起来没有那么冗长了。

知识总结

模拟事件操作

  • 通过.trigger()方法就可以完成模拟事件的操作
  • .trigger()方法简写形式:不带参数.事件()方法,例如不带参数的的.click()方法就是在带DOM元素上触发点击事件.

键盘事件可以分为两类:

  1. 直接对键盘按键给出响应的事件(keyupkeydown)
  2. 对文本输入给出响应的事件(keypress)。

默认情况下具有键盘驱动行为的元素才可以获得焦点
如果想知道用户按了哪个键,应该侦听keyupkeydown事件;如果想知道用户输入的是什么字符,应该侦听keypress事件
事件对象的.which属性包含着被按下的那个键的标识符。对于字母键而言,这个标识符就是相应大写字母的ASCII

4.1 修改内联 CSS

在接触漂亮的jQuery效果之前,有必要先简单地谈一谈CSS。在前几章中,为了修改文档的外观,我们都是先在单独的样式表中为类定义好样式,然后再通过jQuery来添加或者移除这些类。
一般而言,这都是为HTML应用CSS的首选方式,因为这种方式不会影响样式表负责处理页面表现的角色。但是,在有些情况下,可能我们要使用的样式没有在样式表中定义,或者通过样式表定义不是那么容易。针对这种情况,jQuery提供了.css()方法。

.css方法描述

这个.css()方法集getter方法和setter方法于一身。通过.css()既可以设置CSS属性也可以获取CSS属性值

如何取样式属性的值

获取单个属性值

取得某个样式属性的值,可以传递一个字符串形式的属性名作为.css()方法的参数,然后就得到一个字符串形式的属性值。

获取多个属性值

取得多个样式属性的值,可以传入属性名的数组作为作为.css()方法的参数,这样得到的是属性和值构成的对象

对于由多个单词构成的属性名,jQuery既可以解释连字符版的CSS表示法(如background-color) ,也可以解释驼峰大小写形式的DOM表示法(如backgroundColor)。

1
2
3
4
//取得单个属性的值,返回"value" 
.css('property')
//取得多个属性的值,返回{"property1": "value1", "property-2": "value2"}
.css(['property1', 'property-2'])

如何设置样式属性

设置样式属性时,.css()方法能够接受的参数有两种:

设置一个样式属性

传递两个参数作为.css()方法的参数,第一个参数是样式属性,第二个参数是该样式属性的值,示例代码如下:

1
2
//单个属性及其值 
.css('property', 'value')

设置多个样式属性

传递一个由属性-属性值对构成的对象作为.css()方法的参数,示例代码如下:

1
2
3
4
5
//属性-值对构成的对象 
.css({
property1: 'value1',
'property-2': 'value2'
})

对象字面量

这些键值对的集合对象字面量,是在代码中直接创建的JavaScript对象。

一般来说,数字值不需要加引号字符串值需要加引号。由于属性名是字符串,所以属性通常是需要加引号的。但是,如果对象字面量中的属性名是有效的JavaScript标识符,比如使用驼峰大小写形式的DOM表示法时,则可以省略引号。

使用.css()的方式与前面使用.addClass()的方式相同——将它连缀jQuery对象后面,这个jQuery对象包含一组DOM元素。为此,我们仍以第3章的样式转换器为例,但这次使用的HTML稍有不同:
在通过链接的样式表为这个文档添加了一些基本样式规则之后,初始的页面如图4-1所示。

有了这些代码之后,单击BiggerSmaller按钮,会增大或缩小<div class="speech">中文本的字体大小,而单击Default按钮,则会把<div class="speech">中文本的字体重置为初始大小。

本文重点

.css()方法

jQuery提供的.css()方法用于要使用的样式没有在样式表中定义,或者通过样式表定义不是那么容易的情况下.
通过.css()既可以设置样式属性也可以获取样式属性的值.

获取样式属性值

获取单个样式属性值

取得某个样式属性的值,可以传递一个字符串形式的属性名作为.css()方法的参数,然后就得到一个字符串形式的属性值。

获取多个样式属性值

取得多个样式属性的值,可以传入属性名的数组作为.css()方法的参数,这样得到的是属性和值构成的对象

设置样式属性

设置一个样式属性

传递两个参数作为.css()方法的参数,第一个参数是样式属性,第二个参数是该样式属性的值

设置多个样式属性

传递一个由属性-属性值对构成的对象作为.css()方法的参数

第4章 样式与动画

通过jQuery,我们不仅能够轻松地为页面操作添加简单的视觉效果,甚至能创建更精致的动画。
jQuery效果确实能增添艺术性,一个元素逐渐滑入视野而不是突然出现时,带给人的美感是不言而喻的。此外,当页面发生变化时,通过效果吸引用户的注意力,则会显著增强页面的可用性(在Ajax应用程序中尤其常见)。
本章,我们将学习以下内容:

  1. 动态修改元素的样式;
  2. 通过各种内置效果隐藏和显示元素;
  3. 创建自定义的元素动画;
  4. 实现一个接一个的效果排队。