一、jQuery 能做什么?
- 取得文档中的元素;如:获取选择器等
- 修改页面的外观;如:操作css
- 改变文档的内容;如:追加、修改文档等
- 响应用户的交互操作;如:jQuery事件
- 为页面添加动态效果;如:jQuery动画
- 无需刷新页面从服务器获取信息;如:简化Ajax操作
- 简化常见的 JavaScript 任务。如:jQuery改进了对基本的 JavaScript 数据结构的操作(迭代和数组操作等)。
二、通过使用$(document).ready()
方法,jQuery支持我们预定在DOM加载完毕后调用某个函数,而不必等待页面中的图像加载。尽管不使用jQuery,也可以做到这种预定,但$(document).ready() 为我们提供了很好的跨浏览器解决方案,涉及如下功能:
- 尽可能使用浏览器原生的DOM就绪实现,并以 window.onload 事件处理程序作为后备;
- 可以多次调用 $(document).ready() 并按照调用它们的顺序执行;
- 即便是在浏览器事件发生之后把函数传给 $(document).ready() ,这些函数也会执行;
- 异步处理事件的预定,必要时脚本可以延迟执行;
- 通过重复检查一个几乎与DOM同时可用的方法,在较早版本的浏览器中模拟DOM就绪事件。
.ready() 方法的参数可以是一个已经定义好的函数的引用,也可以是一个匿名函数。
function addHighlightClass() {
$('div.poem-stanza').addClass('highlight');
}
$(document).ready(addHighlightClass);
三、jQuery最强大的特性之一就是它能够简化在DOM中选择元素的任务。DOM(Document Object Model,文档对象模型)充当了JavaScript与网页之间的接口;它以对象网络而非纯文本的形式来表现HTML的源代码。
四、 $() 函数接受CSS选择符作为参数,充当一个工厂,返回包含页面中对应元素的jQuery对象。
五、jQuery中的多数自定义选择符都可以让我们从已经找到的元素中选出一或多个元素。自定义选择符通常跟在一个CSS选择符后面,基于已经选择的元素集的位置来查找元素。自定义选择符的语法与CSS中的伪类选择符语法相同,即选择符以冒号( : )开头。
六、告诉大家一条通用的经验法则:要尽可能使用CSS规范中规定的选择符,除非没有,可使用jQuery的自定义选择符。同样,在修改选择符之前,也要记住只在确实有必要提升性能的情况下再去提升。至于测量修改选择符之后的性能提升了多少,可以使用类似http://jsperf.com/所提供的基准测试工具。
七、众所周知,要得到一个jQuery对象的实例,需要向 $() 函数传入一个选择符表达式。而得到的对象是一个数组结构,其中包含着与该选择符匹配的每个DOM元素的引用。可是我们并不知道的是,这个对象中还隐藏着其他一些属性。
比如 .context 属性中包含着一个DOM节点(通常是 document )的引用,搜索就是从这个节点开始的;
比如 .selector 属性中保存着创建最终对象的选择符表达式。在调用 .on() 等事件委托方法时,这两个属性就会派上用场。
不过,在调用某个DOM遍历方法时,则会用上第三个属性: .prevObject 。这个属性中保存着调用遍历方法的那个jQuery对象。
$(document).ready(function() {
var $cell = $('#release').nextAll();
$cell.addClass('highlight');
console.log($cell.context);
console.log($cell.selector);
console.log($cell.prevObject);
});
// $cell.context 为:Document
// $cell.selector 为:#release.nextAll()
// $cell.prevObject 为:[td]
八、无论什么时候,都应该把简化代码的编写和维护工作放在首位。只有在性能确确实实是一个可以感知的问题时,再考虑牺牲可读性来优化代码执行速度。
虽然应该避免过早优化,但最低限度地重复选择符和遍历方法则始终是值得提倡的。因为这些操作都可能会耗费较多时间,用得越少越好。而要避免重复,有两个策略值得讨论,那就是连缀和缓存对象。
九、当触发任何事件处理程序时,关键字 this 引用的都是携带相应行为的DOM元素。前面我们谈到过,$()
函数可以将DOM元素作为参数,而 this 关键字是实现这个功能的关键 。 不过在事件处理程序中使用 $(this) ,可以为相应的元素创建jQuery对象,然后就如同使用CSS选择符找到该元素一样对它进行操作。
十、事件处理程序中的变量 event 保存着事件对象。
- event.target 属性保存着发生事件的目标元素。通过 .target,可以确定DOM中首先接收到事件的元素(即实际被单击的元素)。而且,我们知道 this 引用的是处理事件的DOM元素。
- event.stopPropagation(); 该方法可以完全阻止事件冒泡。
- event.preventDefault(); 方法则可以在触发默认操作之前终止事件 .
- event.pageX,event.pageY 属性保存着鼠标在页面的坐标。
- event.which 属性包含着键盘事件中被按下的那个键的标识符,对于字母键而言,这个标识符就是相应大写字母的ASCII值。可通过String.fromCharCode()进行转字符码。
- event.type 属性保存着发生事件的事件名,如click、dblclick等。
事件传播和默认操作是相互独立的两套机制,在二者任何一方发生时,都可以终止另一方。
如果想要同时停止事件传播和默认操作,可以在事件处理程序中返回 false ,这是对在事件对象上同时调用 .stopPropagation() 和 .preventDefault() 的一种简写方式。
十一、内置的事件委托功能。
.on() 方法可以接受相应参数实现事件委托,如果给 .on() 方法传入的第二个参数是一个选择符表达式,jQuery会把 click 事件处理程序绑定到 #switcher 对象,同时比较 event.target 和选择符表达式(这里的 ‘button’ )。如果匹配,jQuery会把 this 关键字映射到匹配的元素,否则不会执行事件处理程序。
$('#switcher').on('click', 'button', function() {
var bodyClass = event.target.id.split('-')[1];
$('body').removeClass().addClass(bodyClass);
$('#switcher button').removeClass('selected');
$(this).addClass('selected');
});
十二、为事件处理程序添加命名空间
调用 .off() 方法可以移除事件处理程序,但这样会移除所有的相关事件处理程序,应该让.off() 方法有针对性的移除,所以要达成目标的一种方式是使用事件命名空间,即在绑定事件时引入附加信息,以便将来识别特定的处理程序。要使用命名空间,需要退一步使用绑定事件处理程序的非简写方法,即 .on() 方法本身。
我们为 .on() 方法传递的第一个参数,应该是想要截获的事件的名称。不过,在此可以使用一种特殊的语法形式,即对事件加以细分。
$(document).ready(function() {
$('#switcher').on('click.collapse', function(event) {
if (!$(event.target).is('button')) {
$('#switcher button').toggleClass('hidden');
}
});
$('#switcher-narrow, #switcher-large').click(function() {
$('#switcher').off('click.collapse');
});
});
对于事件处理系统而言,后缀 .collapse 是不可见的。换句话说,这里仍然会像编写 .on(‘click’) 一样,让注册的函数响应单击事件。但是,通过附加的命名空间信息,则可以解除对这个特定处理程序的绑定,同时不影响为按钮注册的其他单击处理程序。
十三、键盘事件可以分为两类:直接对键盘按键给出响应的事件( keyup 和 keydown )和对文本输入给出响应的事件( keypress )。
有一条实践经验还是比较可靠的:如果想知道用户按了哪个键,应该侦听 keyup 或 keydown 事件;如果想知道用户输入的是什么字符,应该侦听 keypress 事件。
键盘事件的目标是当前拥有键盘焦点的元素。元素的焦点可能会在几种情况下转移,包括单击鼠标和按下Tab键。并非所有元素都可以获得焦点,只有那些默认情况下具有键盘驱动行为的元素,如表单字段、链接,以及指定了 tabIndex属性的元素才可以获得焦点。
十四、对象字面量
一般来说,数字值不需要加引号而字符串值需要加引号。由于属性名是字符串,所以属性通常是需要加引号的。但是,如果对象字面量中的属性名是有效的JavaScript标识符,比如使用驼峰大小写形式的DOM表示法时,则可以省略引号。
//属性-值对构成的对象
.css({
property1: 'value1',
'property-2': 'value2'
})
十五、jQuery还提供了一个强大的 .animate() 方法,用于创建控制更加精细的自定义动画。 .animate() 方法有两种形式,第一种形式接收以下4个参数。
- 一个包含样式属性及值的对象。
- 可选的时长参数:既可以是预置的字符串,也可以是毫秒数值。
- 可选的缓动(easing)类型。
- 可选的回调函数。
// 第一种形式
.animate({property1: 'value1', property2: 'value2'},
duration, easing, function() {
alert('The animation is finished.');
}
);
// 第二种形式
.animate({
property1: 'value1',
property2: 'value2'
}, {
duration: 'value',
easing: 'value',
specialEasing: {
property1: 'easing1',
property2: 'easing2'
},
complete: function() {
alert('The animation is finished.');
},
queue: true,
step: callback
});
.animate() 方法针对CSS属性提供了方便简写值: ‘show’ 、 ‘hide’和 ‘toggle’ ,以便在简写方法不适用时提供另一种简化 .slideToggle() 等内置效果方法的方式。
十六、obj.height()、obj.outWidth() 等方法返回的是不带单位的数值。
十七、把非效果方法添加到队列中的一种方式,就是使用 .queue() 方法。在该方法的回调函数里要添加的这个 next () 方法可以让队列在中断的地方再接续起来。
$switcher
.css({position: 'relative'})
.fadeTo('fast', 0.5)
.slideUp('slow')
.queue(function(next) {
$switcher.css({backgroundColor: '#f00'});
next();
})
.slideDown('slow');
十八、随着在应用效果时需要考虑的变化的增多,要记住这些效果是同时发生还是按顺序发生会变得越来越困难。
- 一组元素上的效果:
当在一个.animate()方法中以多个属性的方式应用时,是同时发生的;
当以方法连缀的形式应用时,是按顺序发生的(排队效果)——除非queue选项值为false。 - 多组元素上的效果:
默认情况下是同时发生的;
当在另一个效果方法或者在 .queue() 方法的回调函数中应用时,是按顺序发生的(排队效果)。
十九、HTML属性与DOM属性
HTML属性是指页面标记中放在引号中的值,而DOM属性则是指通过JavaScript能够存取的值。
大多数情况下,HTML属性与对应的DOM属性的作用都是一样的,jQuery可以帮我们处理名字不一致的问题。可是,有时候我们的确需要留意这两种属性的差异。某些DOM属性,例如nodeName 、 nodeType 、 selectedIndex 和 childNodes ,在HTML中没有对应的属性,因此通过 .attr() 方法就没有办法操作它们。此外,数据类型方面也存在差异,比如HTML中的 checked属性是一个字符串,而DOM中的 checked 属性则是一个布尔值。对于布尔值属性,最后是测试DOM属性而不是HTML属性,以确保跨浏览器的一致行为。
在jQuery中,可以通过 .prop() 方法取得和设置DOM属性;通过 .attr() 方法取得和设置HTML属性。
HTML属性与DOM属性差别最大的地方,恐怕就要数表单控件的值了。比如,文本输入框的value 属性在DOM中的属性叫 defaultValue ,DOM中就没有 value 属性。而选项列表( select )元素呢,其选项的值在DOM中通常是通过 selectedIndex 属性,或者通过其选项元素的selected 属性来取得。
由于存在这些差异,在取得和设置表单控件的值时,最好不要使用 .attr() 方法。而对于选项列表呢,最好连 .prop() 方法也不要使用。那使用什么呢,建议使用jQuery提供的 .val()方法:
// 取得文本输入框的当前值
var inputValue = $('#my-input').val();
// 取得选项列表的当前值
var selectValue = $('#my-select').val();
// 设置单选列表的值
$('#my-single-select').val('value3');
// 设置多选列表的值
$('#my-multi-select').val(['value1', 'value2']);
二十、插入操作
.insertBefore() 在现有元素外部、之前添加内容;
.insertAfter() 在现有元素外部、之后添加内容;
.prependTo() 在现有元素内部、之前添加内容;
.appendTo() 在现有元素内部、之后添加内容。
二十一、通常使用“ + ”操作符来拼接字符串,但如果要拼接的字符串太多,那看起来就会很乱。使用 .join() 方法可以避免因要拼接的字符串过多而引起混乱。
// 下面的两行代码结果相同
var str = 'a' + 'b' + 'c';
var str = ['a', 'b', 'c'].join('');
二十二、使用 .clone() 连同事件一起复制
在默认情况下, .clone() 方法不会复制匹配的元素或其后代元素中绑定的事件。不过,可以为这个方法传递一个布尔值参数,将这个参数设置为 true ,就可以连同事件一起复制,即 .clone(true) 。这样一来,就可以避免每次复制之后还要手工重新绑定事件的麻烦。
二十三、Ajax(异步JavaScript和XML)
从根本上来说,一个Ajax解决方案中涉及如下技术。
JavaScript:处理与用户及其他浏览器相关事件的交互,解释来自服务器的数据,并将其呈现在页面上。
XMLHttpRequest :这个对象可以在不中断其他浏览器任务的情况下向服务器发送请求。
文本文件:服务器提供的XML、HTML或JSON格式的文本数据。
传输数据的方式:在不需要与其他应用程序共享数据的情况下,以HTML片段提供外部数据一般来说是最简单的。如果数据需要重用,而且其他应用程序也可能因此受到影响,那么在性能和文件大小方面具有优势的JSON通常是不错的选择。而当远程应用程序未知时,XML则能够为良好的互操作性提供最可靠的保证。
二十四、JSON(JavaScript Object Notation,JavaScript对象表示法),通过这种表示法能够方便地取代数据量庞大的XML格式。
{
"key": "value",
"key 2": [
"array",
"of",
"items"
]
}
在对象字面量和数组字面量的基础上,JSON格式的语法具有很强的表达能力,但对其中的值也有一定的限制。例如,JSON规定所有对象键以及所有字符串值,都必须包含在双引号中。而且,函数也不是有效的JSON值。由于存在这些限制,开发人员最好不要手工编辑JSON,而应该用服务器端语言来生成。
要取得 JSON 数据,可以使用 $.getJSON()
方法,这个方法会在取得相应文件后对文件进行处理。在数据从服务器返回后,它只是一个简单的JSON格式的文本字符串。 $.getJSON()
方法会解析这个字符串,并将处理得到的JavaScript对象提供给调用代码。
$.getJSON()
函数可以接受第2个参数(第1个参数为 json 文件),这个参数是当加载完成时调用的函数。Ajax请求都是异步的,回调函数提供了一种等待数据返回的方式,而不是立即执行代码。回调函数也需要一个参数,该参数中保存着返回的数据。
$.getJSON('b.json', function(data) {
});
二十五、 $.each()
函数不操作jQuery对象,它以数组或对象作为第一个参数,以回调函数作为第二个参数。此外,回调函数也需要两个参数,分别是每次循环中数组或对象的 当前索引 和 当前项。
$.each(data, function(entryIndex, entry) {
html += '<div class="entry">';
html += '<h3 class="term">' + entry.term + '</h3>';
html += '<div class="part">' + entry.part + '</div>';
html += '<div class="definition">';
html += entry.definition;
html += '</div>';
html += '</div>';
});
二十六、加载XML文档
$.get() 函数
,这个函数只是取得由URL指定的文件,然后将纯文本格式的数据提供给回调函数。但是,在根据服务器提供的MIME类型知道响应的是XML的情况下,提供给回调函数的将是XML DOM树。
遍历XML文档的方式同HTML文档一样,也可以使用常规的 .find() 、 .filter() 及其他遍历方法。
// requestdata 该参数是一个用来构建查询字符串的键和值的对象
$.get('e.php', requestData, function(data) {
$('#dictionary').html(data);
});
.load() 方法在接收到包含数据的对象参数时,会默认使用 POST 方法发送请求。
// requestdata 该参数是一个用来构建查询字符串的键和值的对象
$('#dictionary').load('e.php', requestData);
.serialize() 方法。这个方法作用于一个jQuery对象,将匹配的DOM元素转换成能够随Ajax请求传递的查询字符串。
二十七、有时候多了解一些调用Ajax方法过程中的HTTP请求也会给我们带来方便。为满足这种需求,jQuery提供了一组函数,通过它们能够为各种与Ajax相关的事件注册回调函数。
.ajaxStart() 、 .ajaxStop() 和 .ajaxError() 方法就是这些“观察员”函数中的三个例子。所有这些“观察员”都是全局性的,因为无论创建它们的代码位于何处,当Ajax通信发生时都需要调用它们。而且这些方法都与 .ready() 方法一样,只能由 $(document) 调用。
当Ajax请求开始且尚未进行其他传输时,会触发 .ajaxStart() 的回调函数。
当最后一次活动请求终止时,则会执行通过 .ajaxStop() 注册的回调函数。
$(document).ready(function() {
var $loading = $('<div id="loading">Loading...</div>')
.insertBefore('#dictionary');
$(document).ajaxStart(function() {
$loading.show();
}).ajaxStop(function() {
$loading.hide();
});
});
二十八、 $.get() 和 .load() 等快捷的Ajax方法并没有提供错误回调参数,因此我们需要找找其他地方是否有解决方案。
除了使用全局的 .ajaxError() 方法,我们还可以利用jQuery的延迟对象系统。
也可以给 .load() 之外的Ajax方法连缀 .done() 、 .always() 和 .fail() 方法,并通过它们添加相应的回调函数即可。
$(document).ready(function() {
$('#letter-e a').click(function(event) {
event.preventDefault();
var requestData = {term: $(this).text()};
$.get('z.php', requestData, function(data) {
$('#dictionary').html(data);
}).fail(function(jqXHR) {
$('#dictionary')
.html('An error occurred: ' + jqXHR.status)
.append(jqXHR.responseText);
});
});
});
jqXHR.responseText 的内容根据服务器的配置不同可能会有所不同;
jqXHR.status 属性中包含着服务器返回的状态码
二十九、JSONP
JSONP无非就是简单的JSON加上了服务器支持,让我们能够向不同的站点发送请求。在请求JSONP数据时,需要提供一个特殊的查询字符串参数,发送请求的脚本就是通过该参数来收获数据的。JSONP服务器可以在认为合适的任何时候调用该参数。对于jQuery API站点而言,这个参数(也是默认的名字)是 callback 。
因为可以使用默认回调函数名,所以通过jQuery发送JSONP请求时,唯一要做的就是说明我们想要的是 jsonp 类型的数据。
$(document).ready(function() {
var $ajaxForm = $('#ajax-form'),
$response = $('#response');
$ajaxForm.on('submit', function(event) {
event.preventDefault();
$.ajax({
url: 'http://book.learningjquery.com/api/',
dataType: 'jsonp',
data: {
title: $('#title').val()
},
success: function(data) {
console.log(data);
}
});
});
});
三十、如果服务器返回错误,那么通过传递给错误回调函数的 jqXHR 对象的 .status 属性,可以检测到该错误。换句话说,使用 jqXHR.status 的值可以对不同的错误给出不同的响应。
然而,服务器错误只有你检测到它的时候才有用。有些错误可以立即检测到,而有些情况则会导致请求到最终错误响应之间产生很长的时间延迟。
在没有既定的服务器端超时机制的情况下,我们可以在客户端强制设定请求的超时。通过给timeout 选项传递一个以毫秒表示的时间值,就相当于告诉 $.ajax(): 如果响应在多长时间内没有返回,那么就调用它自己的 .abort() 方法。
$.ajax({
url: 'http://book.learningjquery.com/api/',
dataType: 'jsonp',
data: {
title: $('#title').val()
},
timeout: 15000,
success: response,
error: function() {
$response.html(failed);
}
});
// 设置了超时时间后,就可以确保在15秒内,要么正常加载数据,要么用户能看到一条错误消息。
三十一、默认情况下,cookie的值将在会话期间保持,直到关闭浏览器标签页为止。此外,默认情况下,cookie还是与设置它的页面关联的。如果想改变这个默认设置,可以为这个函数提供一个选项对象作为第三个参数。这是jQuery插件乃至jQuery核心函数的典型使用模式。
比如,要想让cookie在整个站点中都可以访问到,而且让它在7天之后再过期,就可以像这样来调用函数: $.cookie(‘cyclePaused’, ‘y’, {path: ‘/’, expires: 7}) 。
三十二、jQuery内置的某些功能是通过全局函数提供的。所谓全局函数,实际上就是 jQuery 对象的方法,但从实践的角度上看,它们是位于 jQuery 命名空间内部的函数。核心jQuery库提供的很多全局函数都是实用方法;所谓实用方法,就是一些常用功能的快捷方式,但即使手工编写同样功能的代码也不是很难。数组处理方法 $.each()
、 $.map()
和 $.grep()
都是实用方法。
要向 jQuery 的命名空间中添加一个函数,只需将这个新函数指定为 jQuery 对象的一个属性即可。
(function($) {
$.sum = function(array) {
//在这里添加代码
};
})(jQuery);
三十三、扩展全局jQuery对象
事实上,利用 $.extend() 函数,还可以通过另外一种语法来定义全局函数。
(function($) {
$.extend({
sum: function(array) {
var total = 0;
$.each(array, function(index, value) {
value = $.trim(value);
value = parseFloat(value) || 0;
total += value;
});
return total;
},
average: function(array) {
if ($.isArray(array)) {
return $.sum(array) / array.length;
}
return '';
}
});
})(jQuery);
这样调用 $.extend() 就可以给全局jQuery对象添加属性(如果原来有相同的属性,就会替换原来的属性)。
三十四、 使用命名空间隔离函数
我们的插件在jQuery命名空间中创建了两个独立的全局函数。但这样写有可能污染命名空间。换句话说,其他jQuery插件也可能定义相同的函数名。为了避免冲突,最好的办法是把属于一个插件的全局函数都封装到一个对象中。
(function($) {
$.mathUtils = {
sum: function(array) {
var total = 0;
$.each(array, function(index, value) {
value = $.trim(value);
value = parseFloat(value) || 0;
total += value;
});
return total;
},
average: function(array) {
if ($.isArray(array)) {
return $.mathUtils.sum(array) / array.length;
}
return '';
}
};
})(jQuery)
这个模式的本质是为所有的全局函数又创建了一个命名空间,叫做 jQuery.mathUtils 。
虽然我们还称它们为全局函数,但实际上它们已经成了 mathUtils 对象的方法了,而mathUtils 对象则保存在 jQuery 对象的属性中。结果,在调用它们时就必须得加上插件的名字了:
$.mathUtils.sum(sum);
$.mathUtils.average(average);
使用这种技术(以及足够独特的命名空间),就能够避免全局函数污染命名空间。
选择命名空间
对于仅限于个人使用的函数,一般来说还是把它保存在项目的命名空间中最方便。换句话说,不要保存在 jQuery 命名空间中,而要选择一个我们自己的全局对象。比如说,可以将 ljQ 作为全局对象,那么 $.mathUtils.sum()
和$.mathUtils.average()
就 要 写 成 ljQ.mathUtils.sum()
和 ljQ.math-Utils.average()
了。这样,就可以彻底避免自定义的插件方法与第三方插件方法发生命名冲突。
三十五、添加 jQuery 对象方法
添加全局函数需要以新方法来扩展 jQuery 对象。添加实例方法也与此类似,但扩展的却是 jQuery.fn 对象:
jQuery.fn.myMethod = function() {
alert('Nothing happens.');
};
jQuery.fn 对象是 jQuery.prototype 的别名,使用别名是出于简洁的考虑。一个合理的实例方法应该包含对它的上下文的操作。
在任何插件方法内部,关键字 this 引用的都是当前的jQuery对象。因而,可以在 this 上面调用任何内置的jQuery方法,或者提取它包含的DOM节点并操作该节点。在调用的 .each() 方法内部, this 依次引用每个DOM元素。
除了隐式迭代之外,jQuery用户也应该能够正常使用连缀行为。因而,我们必须在所有插件方法中返回一个jQuery对象,除非相应的方法明显用于取得不同的信息。返回的jQuery对象通常就是 this 所引用的对象。
(function($) {
$.fn.swapClass = function(class1, class2) {
return this.each(function() { var $element = $(this); if ($element.hasClass(class1)) { $element.removeClass(class1).addClass(class2); } else if ($element.hasClass(class2)) { $element.removeClass(class2).addClass(class1); } });
};
})(jQuery);
三十六、由于某些浏览器中的 scroll 事件会在窗口滚动期间重复触发,因此计算过程会不断累积。最终结果就是导致页面忽急忽缓、反应迟顿。
浏览器中有几个原生事件都会频繁触发。最常见的事件有 scroll 、resize 和 mousemove 。为了解决这个问题,就需要节流事件。这个技术会限制一些无谓的计算,即不是每次事件发生都计算,而是选择在部分事件发生时计算。
$(document).ready(function() {
var timer = 0;
$(window).scroll(function() {
if (!timer) {
timer = setTimeout(function() {
checkScrollPosition();
timer = 0;
}, 250);
}
}).trigger('scroll');
});
前面这种节流技术可以说既简单又实用。但是,节流的方案可不止那一种。根据被节流的操作的特点,以及与页面的典型交互方式,我们可以直接给页面创建一个计时器,而不是等事件开始时再创建。
$(document).ready(function() {
var scrolled = false;
$(window).scroll(function() {
scrolled = true;
});
setInterval(function() {
if (scrolled) {
checkScrollPosition();
scrolled = false;
}
}, 250);
checkScrollPosition();
});
与前面的节流代码不同,这个轮询式的方案会调用JavaScript的 setInterval() 函数,每250毫秒检查一次 scrolled 变量的状态。不管什么时候发生滚动事件, scrolled 都会被设置为 true ,以确保在下一次轮询时调用 checkScrollPosition() 。
三十七、中止动画的注意事项
由于 .stop() 方法默认情况下会在动画的当前位置中止动画,因而在使用简写动画方法的情况下,就有可能导致意外的结果。在动画之前,这些简写的动画方法会确定最终的值,然后动态变化到该值。比如说,如果使用 .stop() 在 .slideDown() 动画的中途将其中止,然后调用 .slideUp() 。那么下次再在同一个元素上调用 .slideDown() 时,就只会向下滑动到上一次停止时的高度。为了解决这个问题, .stop() 方法可以接收两个布尔值参数( true/false ),其中第二个参数叫 goToEnd 。如果把这个参数设置为 true ,那么当前动画不仅会停止,而且会立即跳到最终值。当然,这样做的结果就是看起来有点突兀。所以更好的办法是把最终值保存在一个变量中,使用 .animate() 显式变化到该值,而不要依赖jQuery确定的值。
jQuery还有一个方法可以中断动画: .finish() 。这个方法与 .stop(true,true) 效果类似,因为它会清除排队的动画并使当前动画跳到最终值。不过,与 .stop(true, true) 不同的是,它也会使所有排队的动画都跳到各自的最终值。
三十八、全局效果属性
jQuery的效果模块中包含一个非常方便的 $.fx 对象,在需要彻底改变动画的性质时,可以访问这个对象。尽管这个对象的某些属性名不见经传,只为jQuery库本身使用而设计,但另外一些属性则可以供我们在全局层面上修改动画运行的效果。
- 禁用所有效果 我们会在默认情况下提供动画,但在一些低配置设备,比如非智能手机上,就需要禁用这些动画;否则,这些设备中的动画就会显得支离破碎。或者,当用户认为动画会分散其注意力时,也应该允许用户关闭动画。为了实现这个功能,只要简单地把 $.fx.off 属性设置为 true 即可。
- 定义效果时长
$.fx 对象还有一个 speeds 属性。这个属性本身是一个对象,包含三个属性,通过jQuery核心源代码中这一小段可以看出来:
speeds: {
slow: 600,
fast: 200,
//默认速度
_default: 400
}
想添加自定义的速度选项,只要给 $.fx.speeds 添加一个属性即可。比如,执行 $.fx.speeds.crawl = 1200
这行代码之后,就可以在任何动画方法中使用 ‘crawl’ 把动画持续时间设置为1200毫秒:
$(someElement).animate({width: '300px'}, 'crawl');
不过别忘了,使用非 swing 和 linear 的任何缓动函数都需要插件,比如jQuery UI。
三十九、.wrapInner()
方法,这个方法会把一个新元素放到匹配元素的内部,同时包含匹配元素的子元素。
四十、JavaScript内置的 .sort() 方法。这个方法会对数组元素进行就地排序,可以接受一个比较函数作为参数。这个比较函数比较数组中的两个元素,根据哪个元素应该在排序后的数组中排在前面返回正值或负值。注意到,这个方法在默认情况下是按照字母表顺序排序的,升序排列。实际上,对于数值数组还是按照数值大小排序才有意义。为此,可以给 .sort() 方法传入一个比较函数:
arr.sort(function(a,b) {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
四十一、JSONP无非就是简单的JSON加上了服务器支持,让我们能够向不同的站点发送请求。在请求JSONP数据时,需要提供一个特殊的查询字符串参数,发送请求的脚本就是通过该参数来收获数据的。JSONP服务器可以在认为合适的任何时候调用该参数。
四十二、jqXHR 对象
由于不同请求使用的数据传输方式可能不一样,那我们就需要一个公共的接口与这些通信交互。为此, jqXHR 对象提供了这种接口:在 XMLHttpRequest 对象可用的情况下,封装该对象的行为;在 XMLHttpRequest 对象不可用的情况下,则尽可能模拟它。这个对象提供给我们的属性和方法包括:
- 包含返回数据的 .responseText 或 .responseXML ;
- 包含状态码和状态描述的 .status 和 .statusText ;
- 操作与请求一起发送的HTTP头部的 .setRequestHeader() ;
提早中断通信的 .abort() 。
jQuery的所有Ajax方法都会返回 jqXHR 对象,只要把这个对象保存起来,随后就可以方便地使用这些属性和方法。
四十三、Ajax承诺
与标准的 XMLHttpRequest 对象相比, jqXHR 对象有一点非常值得重视,那就是它也是一个承诺对象。在讨论延迟对象时,我们知道可以通过它来设置在某个操作完成后触发的回调函数。Ajax调用就是这样一种操作,而 jqXHR 对象提供了延迟对象所承诺的方法。
使用这些承诺对象的方法,可以重写 $.ajax() 调用,把 success 和 error 回调函数替换成如下所示,
$.ajax({
url: 'http://book.learningjquery.com/api/',
dataType: 'jsonp',
data: {
title: $('#title').val()
},
timeout: 15000
//,success: response,
//error: function() {
// $response.html(failed);
//}
})
.done(response)
.fail(function(jqXHR){
$response.html('状态码:' + jqXHR.status + '=>' + jqXHR.statusText + ' , ' + failed);
})
.always(function(){
$response.removeClass('loading');
});
乍一看,调用 .done() 和 .fail() 与之前的写法相比并没有明显的好处。可是,这两个承诺方法的确是有好处的。第一,可以多次调用这两个方法,根据需要添加多个处理程序。第二,如果把调用 $.ajax() 的结果保存在一个变量中,那么就可以考虑代码的可读性,在后面再添加处理程序。第三,如果在添加处理程序的时候Ajax操作已经完成,就会立即调用该处理程序。第四,我们最好采用与jQuery库中其他代码一致的语法,这带来的好处不言而喻。
使用承诺方法的另一个好处是可以在请求期间添加一个加载指示器,然后在请求完成时或在其他情况下隐藏它。这时候,使用 .always() 方法就非常方便。
四十四、截流 Ajax 请求
// 搜索框输入的时候触发请求,使用截流来避免多次请求 消除抖动
var searchTimeout,
searchDelay = 300;
$('#title').on('keyup', function(event){
console.log(event.which);
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function(){
$ajaxForm.triggerHandler('submit');
}, searchDelay);
});
四十五、定义数据类型转换器
要定义一种新的Ajax数据类型,需要给 $.ajaxSetup() 传递三个参数: accepts 、 contents和 converters 。其中, accepts 属性会添加发送到服务器的头部信息,声明我们的脚本可以理解的特定MIME类型; contents 属性处理数据交换的另一方,它提供一个与响应的MIME类型进行匹配的正则表达式,以尝试自动检测这个元数据当中的数据类型。最后, converters 中包含解析返回数据的函数。
$.ajaxSetup({
accepts: {
yaml: 'application/x-yaml, text/yaml'
},
contents: {
yaml: /yaml/
},
converters: {
'text yaml': function(textValue) {
console.log(textValue);
return '';
}
}
});
$.ajax({
url: 'categories.yml',
dataType: 'yaml'
});
四十六、Ajax 预过滤器
// Ajax 预过滤器
$.ajaxPrefilter(function(options) {
if (/\.yml$/.test(options.url)) {
return 'yaml';
}
});
四十六、JavaScript允许开发人员像传递任何类型的数据一样传递函数。也就是说,JavaScript中的内部函数能够逃脱定义它们的外部函数。
逃脱的方式有很多种。
// 1、将内部函数指定给一个全局变量
var globalVar;
function outerFn() {
console.log('Outer function');
function innerFn() {
console.log('Inner function');
}
globalVar = innerFn;
}
console.log('outerFn():');
outerFn();
console.log('globalVar():');
globalVar();
// 2、通过在父函数中返回值来“营救出”内部函数的引用
function outerFn() {
console.log('Outer function');
function innerFn() {
console.log('Inner function');
}
return innerFn;
}
console.log('var fnRef = outerFn():');
var fnRef = outerFn();
console.log('fnRef():');
fnRef();
// 3、通过对象返回内部函数的引用
function outerFn() {
var outerVar = 0;
function innerFn1() {
outerVar++;
console.log('(1) outerVar = ' + outerVar);
}
function innerFn2() {
outerVar += 2;
console.log('(2) outerVar = ' + outerVar);
}
return {'fn1': innerFn1, 'fn2': innerFn2};
}
var fnRef = outerFn();
fnRef.fn1();
fnRef.fn2();
四十七、当内部函数在定义它的作用域的外部被引用时,就创建了该内部函数的一个闭包。在这种情况下,我们称既不是内部函数局部变量,也不是其参数的变量为自由变量,称外部函数的调用环境为封闭闭包的环境。从本质上讲,如果内部函数引用了位于外部函数中的变量,相当于授权该变量能够被延迟使用。因此,当外部函数调用完成后,这些变量的内存不会被释放,因为闭包仍然需要使用它们。