JavaScript跨浏览器获取页面中相同class节点的方法


Posted in Javascript onMarch 03, 2015

网页开发时,在很多时候我们需要操作相同类名的元素,即class相同的元素。昨天参加笔试,有一道相关的题目没答上来:

JavaScript获取页面中class为test的节点

于是收集了一些相关的资料,在本文中列举了两种我觉得比较好的方法,不足之处,还望大家批评指正。如果大家有更好的方法,希望可以分享。

Solution1 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,Chorme,Firefox 3 and 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>

Solution2 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);

};
Javascript 相关文章推荐
在textarea中显示html页面的javascript代码
Apr 20 Javascript
jquery 弹出登录窗口实现代码
Dec 24 Javascript
js对table的td进行相同内容合并示例详解
Dec 27 Javascript
java、javascript实现附件下载示例
Aug 14 Javascript
JQuery异步获取返回值中文乱码的解决方法
Jan 29 Javascript
正则表达式替换html元素属性的方法
Nov 26 Javascript
jQuery Validation Engine验证控件调用外部函数验证的方法
Jan 18 Javascript
servlet+jquery实现文件上传进度条示例代码
Jan 25 Javascript
JS中验证整数和小数的正则表达式
Oct 08 Javascript
JavaScript ES6中的简写语法总结与使用技巧
Dec 30 Javascript
javascript绘制简单钟表效果
Apr 07 Javascript
原生Javascript+HTML5一步步实现拖拽排序
Jun 12 Javascript
JS实现鼠标点击展开或隐藏表格行的方法
Mar 03 #Javascript
浅谈JavaScript数据类型
Mar 03 #Javascript
JavaScript中property和attribute的区别详细介绍
Mar 03 #Javascript
JS+CSS实现自动改变切换方向图片幻灯切换效果的方法
Mar 02 #Javascript
JS上传图片前实现图片预览效果的方法
Mar 02 #Javascript
JS控制弹出新页面窗口位置和大小的方法
Mar 02 #Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
Mar 02 #Javascript
You might like
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
php桌面中心(四) 数据显示
2007/03/11 PHP
PHP 身份验证方面的函数
2009/10/11 PHP
win7下memCache的安装过程(具体操作步骤)
2013/06/28 PHP
PHP邮件发送类PHPMailer用法实例详解
2014/09/22 PHP
Zend Framework教程之Application用法实例详解
2016/03/14 PHP
PHP序列化/对象注入漏洞分析
2016/04/18 PHP
PHP基于curl实现模拟微信浏览器打开微信链接的方法示例
2019/02/15 PHP
php 多继承的几种常见实现方法示例
2019/11/18 PHP
用Javscript实现表单复选框的全选功能
2007/05/25 Javascript
五个jQuery图片画廊插件 推荐
2011/05/12 Javascript
javascript:window.open弹出窗口的位置问题
2014/03/18 Javascript
Javascript原型链和原型的一个误区
2014/10/22 Javascript
jQuery中element选择器用法实例
2014/12/29 Javascript
跟我学习javascript的最新标准ES6
2015/11/20 Javascript
JavaScript中this的四个绑定规则总结
2016/09/26 Javascript
Vue.js系列之vue-router(上)(3)
2017/01/03 Javascript
扩展bootstrap的modal模态框-动态添加modal框-弹出多个modal框
2017/02/21 Javascript
微信小程序 动态的设置图片的高度和宽度详解及实例代码
2017/02/24 Javascript
nodejs搭建本地服务器并访问文件的方法
2017/03/03 NodeJs
JS身份证信息验证正则表达式
2017/06/12 Javascript
VUE元素的隐藏和显示(v-show指令)
2017/06/23 Javascript
JavaScript事件处理程序详解
2017/09/19 Javascript
图文讲解用vue-cli脚手架创建vue项目步骤
2019/02/12 Javascript
[01:39](回顾)各路豪强针锋相对,几经鏖战四强产生
2014/07/01 DOTA
[06:09]辉夜杯主赛事开幕式
2015/12/25 DOTA
python实现自动登录人人网并采集信息的方法
2015/06/28 Python
Python模块包中__init__.py文件功能分析
2016/06/14 Python
Python 如何提高元组的可读性
2019/08/26 Python
Linux安装Python3如何和系统自带的Python2并存
2020/07/23 Python
俄罗斯连接商品和买家的在线平台:goods.ru
2020/11/30 全球购物
机电工程学生自荐信范文
2013/12/07 职场文书
2014年大学生党员自我评议
2014/09/22 职场文书
政府四风问题整改措施
2014/10/04 职场文书
教师个人事迹材料
2014/12/17 职场文书
关于maven依赖 ${xxx.version}报错问题
2022/01/18 Java/Android