10.1 再谈事件

先来介绍一下我们的示例文档,这个文档最终将成为一个简单的影集。影集中会显示一组照片,单击链接则会显示更多照片。当用户把光标移动到每一张照片上面时,会显示该照片的文本简介,这个功能是使用jQuery的事件系统实现的。影集的HTML代码如下:

  1. <div id="container">
  2. <h1>Photo Gallery</h1>
  3. <div id="gallery">
  4. <div class="photo">
  5. <img src="photos/skyemonroe.jpg">
  6. <div class="details">
  7. <div class="description">The Cuillin Mountains, Isle of Skye, Scotland.</div>
  8. <div class="date">12/24/2000</div>
  9. <div class="photographer">Alasdair Dougall</div>
  10. </div>
  11. </div>
  12. <div class="photo">
  13. <img src="photos/dscn1328.jpg">
  14. <div class="details">
  15. <div class="description">Mt. Ruapehu in summer</div>
  16. <div class="date">01/13/2005</div>
  17. <div class="photographer">Andrew McMillan</div>
  18. </div>
  19. </div>
  20. <div class="photo">
  21. <img src="photos/024.JPG">
  22. <div class="details">
  23. <div class="description">midday sun</div>
  24. <div class="date">04/26/2011</div>
  25. <div class="photographer">Jaycee Barratt</div>
  26. </div>
  27. </div>
  28. <!--此处省略了部分代码-->
  29. </div>
  30. <a id="more-photos" href="pages/1.html">More Photos</a>
  31. </div>

 下载示例代码

如同本书其他HTML、CSS以及JavaScript示例一样,上面的标记只是完整文档的一个片段。如果读者想试一试这些示例,可以从以下地址下载完整的示例代码: Packt Publishing 网站 http://www.packtpub.com/support ,或者本书网站 http://book.learningjquery.com/

在为文档中的照片应用样式,将它们一行三个地排列整齐之后,这个影集的外观就如图10-1所示。

10.1.1 追加数据页面 - 图1

图 10-1

10.1.1 追加数据页面

好了,现在我们想来实现一个常见的任务,那就是让浏览器响应对某个页面元素的单击。在单击 More Photos 链接时,需要执行一次 Ajax 请求,加载下一组照片并将它们追加到 <div id="gallery">,参见代码清单10-1。

代码清单10-1

  1. $(document).ready(function() {
  2. var pageNum = 1;
  3. $('#more-photos').click(function(event) {
  4. event.preventDefault();
  5. var $link = $(this);
  6. var url = $link.attr('href');
  7. if (url) {
  8. $.get(url, function(data) {
  9. $('#gallery').append(data);
  10. });
  11. pageNum++;
  12. if (pageNum < 20) {
  13. $link.attr('href', 'pages/' + pageNum + '.html');
  14. }
  15. else {
  16. $link.remove();
  17. }
  18. }
  19. });
  20. });

此外,还要更新More Photos链接的目标,让它指向包含下一组照片的页面。

代码清单10-2

  1. $(document).ready(function() {
  2. var pageNum = 1;
  3. $('#more-photos').click(function(event) {
  4. event.preventDefault();
  5. var $link = $(this);
  6. var url = $link.attr('href');
  7. if (url) {
  8. $.get(url, function(data) {
  9. $('#gallery').append(data);
  10. });
  11. pageNum++;
  12. if (pageNum < 20) {
  13. $link.attr('href', 'pages/' + pageNum + '.html');
  14. }
  15. else {
  16. $link.remove();
  17. }
  18. }
  19. });
  20. });

.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

  1. $(document).ready(function() {
  2. $('div.photo').hover(function() {
  3. $(this).find('.details').fadeTo('fast', 0.7);
  4. }, function() {
  5. $(this).find('.details').fadeOut('fast');
  6. });
  7. });

这样,当用户光标进入照片区域时,相关信息就会以70%的不透明度淡入显示出来;而当用户光标离开照片时,相关信息则立即淡出。

10.1.1 追加数据页面 - 图2

图 10-2

当然,实现这个任务的方式有很多。由于两个处理程序中有一部分代码完全相同,因此可以把它们组合起来以减少冗余的代码。比如,可以为mouseentermouseleave绑定同一个处理程序,只在两个事件名称之间加一个空格即可,参见代码清单10-4。

代码清单10-4

  1. $(document).ready(function() {
  2. $('div.photo').on('mouseenter mouseleave', function(event) {
  3. var $details = $(this).find('.details');
  4. if (event.type == 'mouseenter') {
  5. $details.fadeTo('fast', 0.7);
  6. } else {
  7. $details.fadeOut('fast');
  8. }
  9. });
  10. });

在同一个处理程序绑定到两个事件的情况下,通过检测事件的类型就可以确定是应该淡入还是应该淡出。而查找<div>的代码对两个事件来说则是相同的,所以这里可以只写一次。

毫无疑问,这个例子经过了精心设计,所以共享的代码才会那么少。不过,在其他情况下,这种技术是可以显著减少代码复杂性的。比如,假设我们在mouseenter事件发生时添加一个类,在mouseleave事件发生时删除它,而不是动态改变不透明度,那么只要像下面这样在处理程序中添加一行代码即可:

  1. $(this).find('.details')
  2. .toggleClass('entered', event.type == 'mouseenter');

无论如何,我们脚本现在已经按照预期运行了——但有一个例外,那就是当用户单击More Photos链接加载了更多照片时,新加载的照片不会响应那两个事件。还记得我们曾在第3章提到的吗,事件处理程序只会添加到调用.on()方法时已经存在的元素上。像通过Ajax调用这样后来添加的元素,不会绑定那些事件。当前,我们针对这个问题给出了两个解决方案:一是在加载了新内容之后,“重新绑定”事件处理程序;二是一开始就把事件绑定到包含元素上,不依赖于事件冒泡。后一个解决方案,也就是本章要跟大家继续讨论的,叫做事件委托