10.1 再谈事件
先来介绍一下我们的示例文档,这个文档最终将成为一个简单的影集。影集中会显示一组照片,单击链接则会显示更多照片。当用户把光标移动到每一张照片上面时,会显示该照片的文本简介,这个功能是使用jQuery的事件系统实现的。影集的HTML代码如下:
<div id="container">
<h1>Photo Gallery</h1>
<div id="gallery">
<div class="photo">
<img src="photos/skyemonroe.jpg">
<div class="details">
<div class="description">The Cuillin Mountains, Isle of Skye, Scotland.</div>
<div class="date">12/24/2000</div>
<div class="photographer">Alasdair Dougall</div>
</div>
</div>
<div class="photo">
<img src="photos/dscn1328.jpg">
<div class="details">
<div class="description">Mt. Ruapehu in summer</div>
<div class="date">01/13/2005</div>
<div class="photographer">Andrew McMillan</div>
</div>
</div>
<div class="photo">
<img src="photos/024.JPG">
<div class="details">
<div class="description">midday sun</div>
<div class="date">04/26/2011</div>
<div class="photographer">Jaycee Barratt</div>
</div>
</div>
<!--此处省略了部分代码-->
</div>
<a id="more-photos" href="pages/1.html">More Photos</a>
</div>
下载示例代码
如同本书其他HTML、CSS以及JavaScript示例一样,上面的标记只是完整文档的一个片段。如果读者想试一试这些示例,可以从以下地址下载完整的示例代码: Packt Publishing 网站 http://www.packtpub.com/support ,或者本书网站 http://book.learningjquery.com/。
在为文档中的照片应用样式,将它们一行三个地排列整齐之后,这个影集的外观就如图10-1所示。
图 10-1
10.1.1 追加数据页面
好了,现在我们想来实现一个常见的任务,那就是让浏览器响应对某个页面元素的单击。在单击 More Photos 链接时,需要执行一次 Ajax 请求,加载下一组照片并将它们追加到 <div id="gallery">
,参见代码清单10-1。
代码清单10-1
$(document).ready(function() {
var pageNum = 1;
$('#more-photos').click(function(event) {
event.preventDefault();
var $link = $(this);
var url = $link.attr('href');
if (url) {
$.get(url, function(data) {
$('#gallery').append(data);
});
pageNum++;
if (pageNum < 20) {
$link.attr('href', 'pages/' + pageNum + '.html');
}
else {
$link.remove();
}
}
});
});
此外,还要更新More Photos链接的目标,让它指向包含下一组照片的页面。
代码清单10-2
- $(document).ready(function() {
- var pageNum = 1;
- $('#more-photos').click(function(event) {
- event.preventDefault();
- var $link = $(this);
- var url = $link.attr('href');
- if (url) {
- $.get(url, function(data) {
- $('#gallery').append(data);
- });
- pageNum++;
- if (pageNum < 20) {
- $link.attr('href', 'pages/' + pageNum + '.html');
- }
- else {
- $link.remove();
- }
- }
- });
- });
在.click()
处理程序中,我们使用变量pageNum
来跟踪要请求的下一个照片页面,使用这个数字来为链接构建新的href
属性。因为pageNum
是在函数外部定义的,因此它的值可以在两次点击的过程中得以保持。在到达最后一页时,就删除这个链接。
渐进增强
这个示例可以离线使用,不需要Web服务器。在实际应用当中,相关数据可能会保存在一个数据库里。服务器端代码在接收到浏览器对一组照片的正常请求时,会返回一个完整的HTML页面,在接收到Ajax请求时,则只返回包含相应照片标记的HTML片段。这样,无论客户端是否支持JavaScript,用户都能正常地看到照片。
此外,还需要考虑使用HTML5的历史记录API,让用户能够把我们用Ajax加载的内容保存为书签。关于这个API的详细信息,请参考Dive into HTML5中的文章(http://diveintohtml5.info/history.html),而使用History插件(https://github.com/browserstate/history.js)实现这个功能也相当简单。
10.1.2 悬停时显示数据
接下来我们要实现的功能,就是在用户鼠标移动到照片上的时候显示照片的详细信息。首先,为了显示这些信息,可以使用.hover()
方法,参见代码清单10-3。
代码清单10-3
$(document).ready(function() {
$('div.photo').hover(function() {
$(this).find('.details').fadeTo('fast', 0.7);
}, function() {
$(this).find('.details').fadeOut('fast');
});
});
这样,当用户光标进入照片区域时,相关信息就会以70%的不透明度淡入显示出来;而当用户光标离开照片时,相关信息则立即淡出。
图 10-2
当然,实现这个任务的方式有很多。由于两个处理程序中有一部分代码完全相同,因此可以把它们组合起来以减少冗余的代码。比如,可以为mouseenter
和mouseleave
绑定同一个处理程序,只在两个事件名称之间加一个空格即可,参见代码清单10-4。
代码清单10-4
$(document).ready(function() {
$('div.photo').on('mouseenter mouseleave', function(event) {
var $details = $(this).find('.details');
if (event.type == 'mouseenter') {
$details.fadeTo('fast', 0.7);
} else {
$details.fadeOut('fast');
}
});
});
在同一个处理程序绑定到两个事件的情况下,通过检测事件的类型就可以确定是应该淡入还是应该淡出。而查找<div>
的代码对两个事件来说则是相同的,所以这里可以只写一次。
毫无疑问,这个例子经过了精心设计,所以共享的代码才会那么少。不过,在其他情况下,这种技术是可以显著减少代码复杂性的。比如,假设我们在mouseenter
事件发生时添加一个类,在mouseleave
事件发生时删除它,而不是动态改变不透明度,那么只要像下面这样在处理程序中添加一行代码即可:
$(this).find('.details')
.toggleClass('entered', event.type == 'mouseenter');
无论如何,我们脚本现在已经按照预期运行了——但有一个例外,那就是当用户单击More Photos链接加载了更多照片时,新加载的照片不会响应那两个事件。还记得我们曾在第3章提到的吗,事件处理程序只会添加到调用.on()
方法时已经存在的元素上。像通过Ajax调用这样后来添加的元素,不会绑定那些事件。当前,我们针对这个问题给出了两个解决方案:一是在加载了新内容之后,“重新绑定”事件处理程序;二是一开始就把事件绑定到包含元素上,不依赖于事件冒泡。后一个解决方案,也就是本章要跟大家继续讨论的,叫做事件委托。