9.1 深入选择与遍历
为了讨论有关高级选择符与遍历的内容,我们需要先编写一些脚本,以便有一些可供进一步讨论更高级选择与遍历的实例依据。这个例子是一个HTML页面,其中包含一组新闻列表项。所有新闻列表项都放在一个表格中,可供我们试验选择行与列的各种不同方法。以下是这个页面的代码片段:
<div id="topics">
Topics:
<a href="topics/all.html" class="selected">All</a>
<a href="topics/community.html">Community</a>
<a href="topics/conferences.html">Conferences</a>
<!-- continued... -->
</div>
<table id="news">
<thead>
<tr>
<th>Date</th>
<th>Headline</th>
<th>Author</th>
<th>Topic</th>
</tr>
</thead>
<tbody>
<tr>
<th colspan="4">2011</th>
</tr>
<tr>
<td>Apr 15</td>
<td>jQuery 1.6 Beta 1 Released</td>
<td>John Resig</td>
<td>Releases</td>
</tr>
<tr>
<td>Feb 24</td>
<td>jQuery Conference 2011: San Francisco Bay Area</td>
<td>Ralph Whitbeck</td>
<td>Conferences</td>
</tr>
<!-- continued... -->
</tbody>
</table>
下载代码示例
如同本书其他HTML、CSS以及JavaScript示例一样,上面的标记只是完整文档的一个片段。如果读者想试一试这些示例,可以从以下地址下载完整的示例 代码: Packt Publishing 网站 http://www.packtpub.com/support ,或者本书网站 http://book.learningjquery.com/。
通过这一小段代码,大致能够看出整个文档的结构。这个表格包含4列,分别表示日期(Data)、标题(Headline)、作者(Author)和主题(Topic)。此外,表格中的某些行又包含年度“子标题”,而非前述这4项,如图9-1所示。
图 9-1
在标题与表格之间,有一组按表格中的新闻主题分类的文字链接。而我们的第一个任务,就是改变这些链接的行为,实现对表格中内容的就地筛选,从而避免点击链接跳转到其他页面。
9.1.1 动态筛选表格内容
为了通过主题链接来筛选表格内容,需要先阻止每个链接的默认行为。同时,还应该向用户反馈当前选择了哪个链接,如代码清单9-1所示。
代码清单9-1
$(document).ready(function() {
$('#topics a').click(function(event) {
event.preventDefault();
$('#topics a.selected').removeClass('selected');
$(this).addClass('selected');
});
});
在用户单击其中某个链接时,先删除所有主题链接的selected
类,然后再把selected
类添加到用户单击的链接上。其中的.preventDefault()
语句用于阻止链接打开新的页面。
接下来,就要执行实际的筛选操作了。解决这个问题的第一步,就是隐藏所有不包含相关主题的表格行,如代码清单9-2所示。
代码清单9-2
$(document).ready(function() {
$('#topics a').click(function(event) {
event.preventDefault();
var topic = $(this).text();
$('#topics a.selected').removeClass('selected');
$(this).addClass('selected');
$('#news tr').show();
if (topic != 'All') {
$('#news tr:has(td):not(:contains("' + topic + '"))')
.hide();
}
});
});
在此,我们把链接的文本保存在变量topic
中,以便用它与表格中的文本进行比较。接下来,先显示表格的所有行,而如果主题不是All
则隐藏所有无关的行。用于隐藏无关的行的选择符有些复杂,因此有必要在这里讨论一下:
#news tr:has(td):not(:contains("topic"))
这个选择符的一开始很直观,就是用#news tr
找到表格中的所有行。在接下来筛选元素时,使用自定义选择符:has()
。这个选择符从当前被选中的元素中挑选出那些包含指定元素的元素。在这里,我们排除了那些标题行(例如包含年度的行),因为它们都不包含<td>
单元格。
在找到包含实际内容的表格行之后,还需要找出哪些行与用户选择的主题相关。而自定义选择符:contains()
只会匹配那些某个单元格中包含指定文本的行,在它的外面再加上:not()
选择符,也就得到了不包含相关主题的表格行,最后把这些行隐藏起来就好了。
以上代码基本上可以使用,但是新闻标题中不能包含主题文本。也就是说,我们必须考虑主题文本包含在某个新闻标题文本中的可能性。为了排除这种情况,需要针对每一行多做一些检测,如代码清单9-3所示。
代码清单9-3
$(document).ready(function() {
$('#topics a').click(function(event) {
event.preventDefault();
var topic = $(this).text();
$('#topics a.selected').removeClass('selected');
$(this).addClass('selected');
$('#news').find('tr').show();
if (topic != 'All') {
$('#news').find('tr:has(td)').not(function() {
return $(this).children(':nth-child(4)').text() == topic;
}).hide();
}
});
});
这些新代码用DOM遍历方法代替了某些复杂的选择符表达式。首先,.find()
方法所起到的作用正如之前代码中#news
与tr
之间的空格。但是,.not()
方法在这里完成的任务则是之前的:not()
没有做到的。与我们在第2章介绍的.filter()
方法类似,.not()
可以接收一个回调函数,该函数将在检测每个元素的时候调用。如果这个函数返回true
,那么被检测的元素就会被排除在结果集之外。
选择符与遍历方法
使用选择符还是使用与其对应的遍历方法,最终可能会导致性能上的差异。本章后面还会继续深入讨论这个话题。
在.not()
方法的筛选函数中,我们检测每一行的子元素,查找其第4个子元素(即位于Topic
列的单元格)。对第4个子元素中的文本简单地作一下测试,我们就可以知道是否应该隐藏当前这一行,结果如图9-2所示。
图 9-2
9.1.2 为表格行添加条纹效果
在第2章学习某个选择符的时候曾探讨过为表格中的行交替应用不同颜色的方式。我们知道,使用:even
和:odd
自定义选择符可以迅速实现这一效果,而使用CSS原生的:nth-child()
伪类也可以实现同样的效果,如代码清单9-4所示。
代码清单9-4
$(document).ready(function() {
$('#news').find('tr:nth-child(even)').addClass('alt');
});
这个直观的选择符会找到每个偶数行,而每年的新闻都位于它们自己的<tbody>
元素中,因此交替效果会在每个子部分中重新开始,如图9-3所示。
图 9-3
如果想实现更复杂一些的行条纹,可以尝试每两行一组地应用alt
类。换句话说,给前两行添加这个类,后两行不加,依此类推。为此,需要再用到筛选函数,参见代码清单9-5。
代码清单9-5
$(document).ready(function() {
$('#news tr').filter(function(index) {
return (index % 4) < 2;
}).addClass('alt');
});
在第2章中学习的.filter()
和代码清单9-3:.not()
方法的例子中,筛选函数会检测(关键字this
中包含的)每一个元素,决定它们是否包含在最终的结果集中。在这里,判断是否应该包含某个元素并不需要元素本身的信息。只要知道它们在原始结果集中的位置即可。这个位置信息就是通过一个参数传入到筛选函数中的,我们给这个参数起名为index
。
好了,index
参数目前保存的是每个元素从0开始计数的位置。有了这个数据,就可以使用求模(%)操作符来确定当前的两个元素是否需要添加alt
类。这样就可以实现每两行交替变换一次的条纹效果。
不过,还是有两个小问题需要解决。因为没有再使用:nth-child()
伪类,所以交替效果不会在每个<tbody>
元素中分别开始。同时,为了保证外观的一致,还要跳过表格中的标题行。这些问题只要修改代码中的两个地方就可以解决,如代码清单9-6所示。
代码清单9-6
$(document).ready(function() {
$('#news tbody').each(function() {
$(this).children().has('td').filter(function(index) {
return (index % 4) < 2;
}).addClass('alt');
});
});
为了独立地处理每一组表格行,可以使用.each()
来循环遍历每一个<tbody>
元素。在循环内部,像代码清单9-3中一样使用.has()
排除子标题行即可,结果如图9-4所示。
图 9-4
9.1.3 组合筛选与条纹
高级的表格条纹效果还不错,然而只要一按照主题来筛选,奇怪的现象就出来了。为了让这两个功能可以完美地共存,必须在每次筛选之后重新应用条纹效果。此外,在应用alt
类的时候,还要考虑某些行当前是否隐藏了。代码清单9-7展示了完整的代码。
代码清单9-7
- $(document).ready(function() {
- function stripe() {
- $('#news').find('tr.alt').removeClass('alt');
- $('#news tbody').each(function() {
- $(this).children(':visible').has('td')
- .filter(function(index) {
- return (index % 4) < 2;
- }).addClass('alt');
- });
- }
- stripe();
- $('#topics a').click(function(event) {
- event.preventDefault();
- var topic = $(this).text();
- $('#topics a.selected').removeClass('selected');
- $(this).addClass('selected');
- $('#news').find('tr').show();
- if (topic != 'All') {
- $('#news').find('tr:has(td)').not(function() {
- return $(this).children(':nth-child(4)').text() == topic;
- }).hide();
- }
- stripe();
- });
- });
以上代码整合了代码清单9-3中的筛选代码和刚才的条纹代码,而且定义了一个名为stripe()
的函数。文档加载完毕后会立即调用stripe()
函数,每次单击主题链接时也会调用一次该函数。在这个函数中,一方面删除不再需要的alt
类,另一方面将选择的行限制为当前可见的那些行。这里用到的伪类:visible
(及其对应的伪类:hidden
)非常重要,它会排除由于各种原因隐藏的元素,包括display
值为none
以及width
和height
属性被设置为0。图9-5展示了组合筛选与条纹之后的效果。
图 9-5
9.1.4 更多选择符与遍历方法
本书全部示例加在一块,也不可能穷尽使用jQuery在页面中查找元素的所有方法。它为我们提供的选择符和DOM遍历方法实在太丰富了,每一种都可以在适当的情况下派上用场。
为了找到满足需求的选择符和方法,可以查阅一些资源。本书末尾的快速参考中列出了每一种选择符和方法。不过,要想查找更详细的介绍,建议读者参考更全面的手册。这里有所有选择符的介绍:http://api.jquery.com/category/selectors/,而这里有遍历方法的介绍:http://api.jquery.com/category/traversing/。