Pure DOM
通常先使用getElementsByTagName("*")取出文档中所有元素,然后进行遍历,使用正则表达式找出匹配的元素放入一个数组返回。由于IE5不支持document.getElementsByTagName("*"),要使用分支document.all以防错误。
The Ultimate getElementsByClassName方案,作者为Robert Nyman,05年实现,可见老外许多东西在很早以前就走得很远了。
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|$)" ); |
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。
var getElementsByClass = function (searchClass,node,tag) { |
var classElements = new Array(); |
var els = node.getElementsByTagName(tag); |
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]; |
还有个更古老级的,我从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 classNames = child.className.split( ' ' ); |
for ( var j = 0; j < classNames.length; j++) { |
if (classNames[j] == className) { |
DOM Tree Walker
使用document.createTreeWalker,这是个比较不常用的二级DOM方法。可惜IE全系列歇菜。
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 node = treeWalker.nextNode(); |
returnElements.push(node); |
node = treeWalker.nextNode(); |
XPath
更加新式时髦的技术。
下面取自Prototype.js框架。
document.getElementsByClassName = function (className, parentElement) { |
if (Prototype.BrowserFeatures.XPath) { |
var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]" ; |
return document._getElementsByXPath(q, parentElement); |
var children = ($(parentElement) || document.body).getElementsByTagName( '*' ); |
var elements = [], child; |
for ( var i = 0, length = children.length; i < length; i++) { |
if (Element.hasClassName(child, className)) |
elements.push(Element.extend(child)); |
由于这个是不能运行的,我们修改如下:
var getElementsByClassName = function (searchClass,node,tag) { |
var classes = searchClass.split( " " ), |
namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null , |
for ( var j=0, jl=classes.length; j<jl; j+=1){ |
patterns += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]" ; |
elements = document.evaluate( ".//" + tag + patterns, node, namespaceResolver, 0, null ); |
elements = document.evaluate( ".//" + tag + patterns, node, null , 0, null ); |
while ((_node = elements.iterateNext())) returnElements.push(_node); |
当然如果游览器原生支持,就用原生的。
各主流游览器的支持情况 |
---|
IE8 | IE7 | IE6 | FF3 | FF2 | Saf3 | Op9 | Op10 | Chrome |
---|
N | N | N | Y | N | Y | Y | Y | Y |
速度比较一览
综合以上方案,我得出了一个最理想的实现——兼容IE5,让后面两个参数是可选的,能原生的原生,利用字面量与倒序循环提高效率……
var getElementsByClassName = function (searchClass,node,tag) { |
if (document.getElementsByClassName){ |
return document.getElementsByClassName(searchClass) |
var els = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag); |
searchClass = searchClass.replace(//-/g, "//-" ); |
var pattern = new RegExp( "(^|//s)" +searchClass+ "(//s|$)" ); |
if (pattern.test(els[i].className) ) { |
returnElements.push(els[i]); |
用法:
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){ |
return document.getElementsByClassName(searchClass) |
var classes = searchClass.split( " " ), |
elements = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag), |
patterns.push( new RegExp( "(^|//s)" + classes[i] + "(//s|$)" )); |
for ( var k=0, kl=patterns.length; k<kl; k++){ |
match = patterns[k].test(current.className); |
if (match) returnElements.push(current); |