DOM 提供了一个名为 getElementById() 的方法,这个方法将返回一个对象,这个对象就是参数 id 所对应的元素节点。另外,getElementByTagName() 方法会返回一个对象的数组,每一个对象分别对应着文档里有给定标签的一个元素。这个方法的参数是 html 标签的名字。现在我们考虑一个问题,能不能通过标签的类名class name来获取该对象呢?下面是这个猜想的程序实现(支持多个class查询和在某个范围内进行查询):
07
function
getElementsByClassName(fatherId,tagName,className){
08
node = fatherId&&document.getElementById(fatherId) || document;
09
tagName = tagName ||
"*"
;
10
className = className.split(
" "
);
11
var
classNameLength = className.length;
12
for
(
var
i=0,j=classNameLength;i<j;i++){
14
className[i]=
new
RegExp(
"(^|\\s)"
+ className[i].replace(/\-/g,
"\\-"
) +
"(\\s|$)"
);
16
var
elements = node.getElementsByTagName(tagName);
18
for
(
var
i=0,j=elements.length,k=0;i<j;i++){
19
var
element = elements[i];
20
while
(className[k++].test(element.className)){
21
if
(k === classNameLength){
22
result[result.length] = element;
好,我们来测试一下:
2
<
span
class
=
"aaa zzz ccc"
></
span
>
3
<
div
class
=
"aaa bbb ccc"
></
div
>
5
<
div
class
=
"aaa bbb ccc"
></
div
>
1
window.onload =
function
(){
2
alert(getElementsByClassName(document,
"div"
,
"aaa ccc"
).length);
3
alert(getElementsByClassName(
"container"
,
"div"
,
"aaa ccc"
).length);
4
alert(getElementsByClassName(
"container"
,
"span"
,
"aaa zzz"
).length);
正确的得到了结果。
有人会问,原生的方法调用效率是最高的,有很多浏览器已经将实现了getElementsByClassName这个方法,那为什这里没有先调用原生的再调用自定义的呢?
对,原生的效率是很高,它支持多个class条件的查询,但是最大的问题是他不支持getElementsByClassName("container","div","aaa ccc"),这种在指定标签中查找指定元素为指定class的情况。所以,这里舍弃了原生的方法调用。
在代码中,你会看到我将数组的length缓存了起来,这样可以提高效率。其实上,在这里有一个很隐蔽的问题,就是数组访问length属性和HtmlCollection访问length有很在的区别。在数组中,length是一个普通的属性,访问时不会进行额外的操作,在来看看HTMLCollection,我们常常将HTMLCollection当作数组来使用,但实际上,它是一个根据DOM结构自动变化的实体对象。每次访问一个HTMLCollection对象的属性时,他都会对DOM内所有的节点进行一次完整的匹配。也就是说,每次访问HtmlCollection对象的length时,都会更新一次集合对象,性能上消耗很大。所以一般情况之下,这种HtmlCollection的循环操作,都是建议缓存length的。
小插曲
这是关于往数组里放元素的方式间,效率比较的问题。来看一下代码:
03
var
start =
new
Date();
04
for
(
var
i=0;i<100000;i++){
09
var
start =
new
Date();
10
for
(
var
i=0;i<100000;i++){
猜猜看,哪种效率更高呢!经过测试,第二种方式的效率要高于第一种。好了,不多说的,这个函数简单实用,代码上我也写了注释,看应该已经没有什么问题了。