document.getElementsByClassName的理想实现
2009-07-24 03:07
531 查看
.title {color:#ff7200;font-size:23px;line-height:28px}
.none {color:#99c;}
.red {color:#f00;}
h4.subject{border:none!important;color:#EE862C;}
/* 细线表格 */
.filament_table {
text-align:center;
background:#000;
color:#fff;
border:1px solid #ebebeb;
border-collapse:collapse;
width:90%;
}
.filament_table thead th{
background:#333;
color:#F80;
}
.filament_table tbody td,.filament_table tbody th {
border:1px solid #ebebeb;
}
.filament_table tbody th {
background:#004040
}
The Ultimate getElementsByClassName方案,作者为Robert Nyman,05年实现,可见老外许多东西在很早以前就走得很远了。
另一个实现,由Dustin Diaz(《JavaScript Design Patterns》的作者)提供,但兼容性不如上面的,不支持IE5。
还有个更古老级的,我从prototype.js1.01版本中找到的,它能支持多个类名的查找(上面两个不行)。它不支持IE5,效率一般般,但作为最早的框架之一,它已经做得很好,其他框架还没有想到这个呢!
下面取自Prototype.js框架。
由于这个是不能运行的,我们修改如下:
当然如果游览器原生支持,就用原生的。
综合以上方案,我得出了一个最理想的实现——兼容IE5,让后面两个参数是可选的,能原生的原生,利用字面量与倒序循环提高效率……
用法:
但它还是不如原生的getElementsByClassName,不能同时检索多个class
.none {color:#99c;}
.red {color:#f00;}
h4.subject{border:none!important;color:#EE862C;}
/* 细线表格 */
.filament_table {
text-align:center;
background:#000;
color:#fff;
border:1px solid #ebebeb;
border-collapse:collapse;
width:90%;
}
.filament_table thead th{
background:#333;
color:#F80;
}
.filament_table tbody td,.filament_table tbody th {
border:1px solid #ebebeb;
}
.filament_table tbody th {
background:#004040
}
各种实现方式
Pure DOM
通常先使用getElementsByTagName("*")取出文档中所有元素,然后进行遍历,使用正则表达式找出匹配的元素放入一个数组返回。由于IE5不支持document.getElementsByTagName("*"),要使用分支document.all以防错误。The Ultimate getElementsByClassName方案,作者为Robert Nyman,05年实现,可见老外许多东西在很早以前就走得很远了。
//三个参数都是必需的,查找一网页中5007个类名为“cell”的元素,IE8历时1828 ~ 1844毫秒, //IE6为4610 ~ 6109毫秒,FF3.5为46 ~ 48毫秒,opera10为31 ~ 32毫秒,Chrome为23~ 26毫秒, //safari4为19 ~ 20毫秒 function getElementsByClassName(oElm, strTagName, strClassName){ var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName); var arrReturnElements = new Array(); strClassName = strClassName.replace(/\-/g, "\\-"); var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)"); var oElement; for(var i=0; i < arrElements.length; i++){ oElement = arrElements[i]; if(oRegExp.test(oElement.className)){ arrReturnElements.push(oElement); } } return (arrReturnElements) }
另一个实现,由Dustin Diaz(《JavaScript Design Patterns》的作者)提供,但兼容性不如上面的,不支持IE5。
//后两参数是可靠的,查找一网页中5007个类名为“cell”的元素,IE8历时78毫秒,IE6历时125~171毫秒 //FF3.5为42 ~ 48毫秒,opera10为31 毫秒,Chrome为22~ 25毫秒,safari4为18 ~ 19毫秒 var getElementsByClass = function(searchClass,node,tag) { var classElements = new Array(); if ( node == null ) node = document; if ( tag == null ) tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)"); for (i = 0, j = 0; i < elsLen; i++) { if ( pattern.test(els[i].className) ) { classElements[j] = els[i]; j++; } } return classElements; }
还有个更古老级的,我从prototype.js1.01版本中找到的,它能支持多个类名的查找(上面两个不行)。它不支持IE5,效率一般般,但作为最早的框架之一,它已经做得很好,其他框架还没有想到这个呢!
//由于这是后期添加的,测试页面已被我删掉,没有做测试…… function getElementsByClassName(className, element) { var children = (element || document).getElementsByTagName('*'); var elements = new Array(); for (var i = 0; i < children.length; i++) { var child = children[i]; var classNames = child.className.split(' '); for (var j = 0; j < classNames.length; j++) { if (classNames[j] == className) { elements.push(child); break; } } } return elements; }
DOM Tree Walker
使用document.createTreeWalker,这是个比较不常用的二级DOM方法。可惜IE全系列歇菜。//查找一网页中5007个类名为“cell”的元素,FF3.5为104 ~ 119毫秒,opera10为230 ~ 265毫秒, //Chrome为119 ~ 128毫秒,safari为28 ~ 32毫秒 var getElementsByClassName = function(searchClass) { function acceptNode(node) { if (node.hasAttribute("class")) { var c = " " + node.className + " "; if (c.indexOf(" " + searchClass + " ") != -1) return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_SKIP; } var treeWalker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, acceptNode, true); var returnElements = []; if (treeWalker) { var node = treeWalker.nextNode(); while (node) { returnElements.push(node); node = treeWalker.nextNode(); } } return returnElements; }
XPath
更加新式时髦的技术。下面取自Prototype.js框架。
document.getElementsByClassName = function(className, parentElement) { if (Prototype.BrowserFeatures.XPath) { var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; return document._getElementsByXPath(q, parentElement); } else { var children = ($(parentElement) || document.body).getElementsByTagName('*'); var elements = [], child; for (var i = 0, length = children.length; i < length; i++) { child = children[i]; if (Element.hasClassName(child, className)) elements.push(Element.extend(child)); } return elements; } };
由于这个是不能运行的,我们修改如下:
//查找一网页中5007个类名为“cell”的元素,FF3.5为33 ~ 48毫秒,opera10为31 ~ 32毫秒, //Chrome为104 ~ 107毫秒,safari为18 ~ 21毫秒 var getElementsByClassName = function(searchClass,node,tag) { node = node || document; tag = tag || '*'; var classes = searchClass.split(" "), patterns = "", xhtmlNamespace = "http://www.w3.org/1999/xhtml", namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null, returnElements = [], elements, _node; for(var j=0, jl=classes.length; j<jl; j+=1){ patterns += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]"; } try { elements = document.evaluate(".//" + tag + patterns, node, namespaceResolver, 0, null); } catch (e) { elements = document.evaluate(".//" + tag + patterns, node, null, 0, null); } while ((_node = elements.iterateNext())) returnElements.push(_node); return returnElements; }
当然如果游览器原生支持,就用原生的。
各主流游览器的支持情况 | ||||||||
---|---|---|---|---|---|---|---|---|
IE8 | IE7 | IE6 | FF3 | FF2 | Saf3 | Op9 | Op10 | Chrome |
N | N | N | Y | N | Y | Y | Y | Y |
速度比较一览
全平台兼顾速度的解决方案
由Robert Nyman提供。var getElementsByClassName = function (className, tag, elm){ if (document.getElementsByClassName) { getElementsByClassName = function (className, tag, elm) { elm = elm || document; var elements = elm.getElementsByClassName(className), nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null, returnElements = [], current; for(var i=0, il=elements.length; i<il; i+=1){ current = elements[i]; if(!nodeName || nodeName.test(current.nodeName)) { returnElements.push(current); } } return returnElements; }; } else if (document.evaluate) { getElementsByClassName = function (className, tag, elm) { tag = tag || "*"; elm = elm || document; var classes = className.split(" "), classesToCheck = "", xhtmlNamespace = "http://www.w3.org/1999/xhtml", namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null, returnElements = [], elements, node; for(var j=0, jl=classes.length; j<jl; j+=1){ classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]"; } try { elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null); } catch (e) { elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null); } while ((node = elements.iterateNext())) { returnElements.push(node); } return returnElements; }; } else { getElementsByClassName = function (className, tag, elm) { tag = tag || "*"; elm = elm || document; var classes = className.split(" "), classesToCheck = [], elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag), current, returnElements = [], match; for(var k=0, kl=classes.length; k<kl; k+=1){ classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)")); } for(var l=0, ll=elements.length; l<ll; l+=1){ current = elements[l]; match = false; for(var m=0, ml=classesToCheck.length; m<ml; m+=1){ match = classesToCheck[m].test(current.className); if (!match) { break; } } if (match) { returnElements.push(current); } } return returnElements; }; } return getElementsByClassName(className, tag, elm); };
综合以上方案,我得出了一个最理想的实现——兼容IE5,让后面两个参数是可选的,能原生的原生,利用字面量与倒序循环提高效率……
//查找一网页中5007个类名为“cell”的元素,IE8历时1828 ~ 1844毫秒, //IE6为125 ~ 172毫秒,IE8为93 ~ 94毫秒,FF3.5为0~1毫秒,opera10为0毫秒,Chrome为1毫秒, //safari4为0毫秒 var getElementsByClassName = function(searchClass,node,tag) { if(document.getElementsByClassName){ return document.getElementsByClassName(searchClass) }else{ node = node || document; tag = tag || '*'; var returnElements = [] var els = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag); var i = els.length; searchClass = searchClass.replace(/\-/g, "\\-"); var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)"); while(--i >= 0){ if (pattern.test(els[i].className) ) { returnElements.push(els[i]); } } return returnElements; } }
用法:
var collections = getElementsByClassName("red");
但它还是不如原生的getElementsByClassName,不能同时检索多个class
<h2 class="red cell title">安装支持</h2> <span class="cell red ">jjj</span> <div class="filament_table red cell">这是DIV</div> #利用 var dd = getElementsByClassName("cell red") ,这三个元素都应该能被检索到!
因此,最最终的方案为:
var getElementsByClassName = function (searchClass, node,tag) { if(document.getElementsByClassName){ var nodes = (node || document).getElementsByClassName(searchClass),result = []; for(var i=0 ;node = nodes[i++];){ if(tag !== "*" && node.tagName === tag.toUpperCase()){ result.push(node) } } return result }else{ node = node || document; tag = tag || "*"; var classes = searchClass.split(" "), elements = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag), patterns = [], current, match; var i = classes.length; while(--i >= 0){ patterns.push(new RegExp("(^|\\s)" + classes[i] + "(\\s|$)")); } var j = elements.length; while(--j >= 0){ current = elements[j]; match = false; for(var k=0, kl=patterns.length; k<kl; k++){ match = patterns[k].test(current.className); if (!match) break; } if (match) result.push(current); } return result; } }
相关文章推荐
- document.getElementsByClassName的理想实现
- document.getElementsByClassName的理想实现
- document.getElementsByClassName的理想实现(@司徒正美 大神)
- 【转】document.getElementsByClassName的理想实现
- document.getElementsByClassName的理想实现
- document.getElementsByClassName 的理想实现
- document.getElementsByClassName的理想实现
- document.getElementsByClassName的理想实现
- getElementsByClassName的理想实现
- IE6,7,8浏览器不支持 document.getElementsByClassName
- 浅谈兼容IE6/7的getElementsByClassName的实现
- document.getElementsByClassName的封装,兼容ie8
- 实现ES5以下兼容,Object.create()、getElementsByClassName()
- Prototype 1.6 边学边译 -- 工具函数 document.getElementsByClassName
- 实现getElementsByClassName
- 进一步优化—IE8以下浏览器不支持document.getElementsByClassName() 访问节点;怎么解决?---封装自己的类名
- getElementsByClassName函数实现获取指定类名的子元素列表
- 通过document.getElementsByName来实现 复选框的全选,全部选,反选
- 解决 IE 不支持 document.getElementsByClassName() 的方法
- getElementsByClassName简单实现