先来看一下代码:(支持多个class查询和在某个范围内进行查询)
07 |
function getElementsByClassName(fatherId,tagName,className){ |
08 |
var 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); |
正确的得到了结果。
关于class属性
1.一个元素可以带有多个class属性值,它们通过空格分隔, 例:class='aa bb cc'。
2.class属性值是可以包含'-'这个字符的,如:class='a-b',特别强调是因为在正则表达式中'-'是一个特殊字符。
3.在javascript中通过的获取class属性值的方式为element.className。
原生的getElementsByClassName
有人会问,原生的方法调用效率是最高的,有很多浏览器已经将实现了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++){ |
10 |
var start = new Date(); |
11 |
for ( var i=0;i<100000;i++){ |
猜猜看,哪种效率更高呢!经过测试,第二种方式的效率要高于第一种。
提醒:
这个版本是不兼容IE5的,以下是我给出的解释:
1.当你从那千分之几的统计数据里,再去疯钱的IE5测试人员的点击和专业程序员对IE5的好奇心,估计就只剩IE5被焚烧后的灰烬了。
2.你要想信灰烬的利用价值趋于零
3.如果到现在还没能说服你,那我想说,你很专业!即然这样,那还有IE3,IE2,IE?FF1,NN4...,我想你应该一并实现了。
一些坦白:我也不怎么说服得了我自已,唉,还是给一个IE5的解决方案吧:
IE5并不支持getElementByTagName("*")的形式,所以在这里要处理一下:
var elements = (tagName==='*'&&node.all)?node.all:node.getElementsByTagName(tagName);
好了,不多说的,这个函数简单实用,代码上我也写了注释,看明白应该已经没有什么问题了