JavaScript跨浏览器获取指定class节点的方法详解

作者:像素猫

时间:2026-10-28

浏览:294

本文介绍在JavaScript中获取具有相同class属性节点的方法。针对较新浏览器支持的getElementsByClassName,文章提供了Jeremy Keuth的简单兼容方案及Robert Nyman的高级实现,后者支持多类名搜索、XPath定位及NodeList转数组,确保在IE6/7/8等旧版浏览器中也能高效获取目标元素。

文章目录

获取相同class节点的方法概述

在网页开发过程中,经常需要操作拥有相同类名的元素,即class属性相同的元素。此前在一次笔试中,我曾遇到一道相关题目未能作答:

如何使用JavaScript获取页面中class名为test的节点?

为此,我整理了一些相关资料,在本文中列举了两种我认为较为实用的方法。如有不足之处,恳请大家批评指正。如果您有更好的解决方案,欢迎分享。

方案一:Jeremy Keuth的简单兼容实现

方案一:Jeremy Keuth方案

Jeremy Keuth在《JavaScript DOM编程艺术》(第2版,英文原名:DOM Scripting-Web Design with JavaScript and the Document Object Model)第三章第四节中介绍了getElementsByClass方法,并探讨了如何在不支持该属性的浏览器(如IE6、IE7和IE8,对此我们深感无奈)中应用该方法。以下摘录并稍作修改:

HTML5 DOM新增了一个通过class属性中的类名来访问元素的方法,即getElementsByClassName。由于该方法较新,部分DOM实现尚未支持,因此使用时需谨慎。我们先看看这个方法的功能,再讨论如何可靠地使用它。 与getElementsByTagName类似,getElementsByClassName仅接受一个参数,即类名:

代码如下:

getElementsByClassName(class)

该方法的返回值与getElementsByTagName类似,返回一个包含相同类名元素的数组。以下代码返回一个数组,其中包含所有类名为“sale”的元素:


代码如下:

document.getElementsByClassName("sale")

使用此方法还可以查找具有多个类名的元素。只需在字符串参数中用空格分隔类名即可。例如,在<script>标签中添加以下代码:


代码如下:

alert(document.getElementsByClassName("sale important").length);

完整代码

代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Shopping List</title>
</head>
<body>
<h1>What to buy</h1>
<p title="a gentle reminder">Don't forget to buy this stuff.</p>
<ul id="purchase">
<li>A thin of beans</li>
<li class="sale">Cheese</li>
<li class="sale important">Milk</li>
</ul>
<script>
alert(document.getElementsByClassName("sale important").length);
</script>
</body>
</html>

您将看到警告框显示1,表示只有一个元素匹配,因为只有一个元素同时带有“important”和“sale”类名。请注意,即使元素class属性中类名的顺序是“sale important”而非参数中指定的“important sale”,也能正常匹配。不仅类名的实际顺序无关紧要,即使元素还带有其他类名也无影响。与使用getElementsByTagName类似,也可以组合使用getElementsByClassName和getElementById。如果您想知道id为purchase的元素中有多少列表项包含test类名,可以先找到该特定对象,再调用getElementsByClassName:

代码如下:

var shopping=document.getElementById("purchase");
var sales = shopping.getElementsByClassName("sale");

这样,sales数组中将只包含位于“purchase”列表中的带有“sales”类的元素。运行以下代码,您将看到sales数组包含两项:

代码如下:

alert(sales.length);

getElementsByClassName方法非常有用,但仅较新的浏览器(Safari 3.1、Chrome、Firefox 3及Opera 9.5以上版本)支持它。为了弥补这一不足,DOM脚本程序员需要使用现有的DOM方法来实现自己的getElementsByClassName,这有点像成人礼。在多数情况下,他们的实现过程与以下getElementsByClassName大致相似,该函数适用于新老浏览器。

代码如下:

function getElementsByClassName(node,classname){
if(node.getElementsByClassName){
return node.getElementsByClassName(classname);
}else{
var results = [];
var elems = node.getElementsByTagName("*");
for(var i=0;i<elems.length;i++){
if(elems[i].className.indexOf(classname)!=-1){
results[results.length]=elems[i];
            }
        }
return results;
    }
}

该getElementsByClassName函数接受两个参数。第一个node表示DOM树中的搜索起点,第二个classname是要搜索的类名。如果传入节点上已存在适当的getElementsByClassName函数,则该新函数直接返回相应的节点列表。如果getElementsByClassName函数不存在,该新函数将循环遍历所有标签,查找带有相应类名的元素。

此方法的缺点是不适用于多个类名。

如果使用此函数来模拟前面获取购物列表的操作,可以这样写:

代码如下:

var shopping=document.getElementById("purchase");
var sales = shopping.getElementsByClassName(shopping,"test");
console.log(sales);

因此,解决文章开头那道题目所用的代码如下:

代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Shopping List</title>
</head>
<body>
<h1>What to buy</h1>
<p title="a gentle reminder">Don't forget to buy this stuff.</p>
<ul id="purchase">
<li>A thin of beans</li>
<li class="sale">Cheese</li>
<li class="sale important">Milk</li>
</ul>
<script>
function getElementsByClassName(node,classname){
if(node.getElementsByClassName){
return node.getElementsByClassName(classname);
}else{
var results = [];
var elems = node.getElementsByTagName("*");
for(var i=0;i<elems.length;i++){
if(elems[i].className.indexOf(classname)!=-1){
results[results.length]=elems[i];
               }
            }
return results;
        }
    }
var body = document.getElementsByTagName("body")[0];
var sales= getElementsByClassName(body,"sales");
console.log(sales);
</script>
</body>
</html>

方案二:Robert Nyman的高级多类名支持方案

方案二:Robert Nyman方案

匹配DOM元素的搜索方法还有很多,但真正高效的却不多。Jeremy Keuth的方案有一个缺点,即不能用于多个类名。2008年,Robert Nyman在文章The Ultimate GetElementsByClassName, Anno 2008中提供了自己的解决方案。早在2005年,Robert就给出了自己的getElementsByClassName函数,2008年时,他修改了部分代码,添加了许多新功能:

1.如果当前浏览器支持getElementsByClassName函数,则调用该原生函数; 2.如果当前浏览器支持则使用XPath;//小飞鱼:一种浏览器内置的定位XML文档的强大方式,不过浏览器支持方面不统一 3.支持多个类名的搜索,不计先后顺序; 4.返回真正的节点数组,而不是原生的一个nodelist。//小飞鱼:原生的getElementsByClassName方法返回的是一个NodeList对象,它很像数组,有length和数字索引属性,但并不是数组,不能用pop,push等数组特有的方法,Robert提供的代码中,将NodeList对象转成了数组。可以将NodeList对象转换成数组的方法:

代码如下:

myList = Array.prototype.slice.call (myNodeList)

这是Robert的方案,有些地方我还不太明白,待我研究一下再来更新。

代码如下:

/*
Developed by Robert Nyman, http://www.robertnyman.com
Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/
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);
};

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章