JavaScript的document.getElementsByClassName在IE中的兼容性问题

77

如何检索包含指定类的元素数组的最佳方法?

我会使用document.getElementsByClassName,但IE不支持它。

所以我尝试了Jonathan Snook的解决方案

function getElementsByClassName(node, classname) {
    var a = [];
    var re = new RegExp('(^| )'+classname+'( |$)');
    var els = node.getElementsByTagName("*");
    for(var i=0,j=els.length; i<j; i++)
        if(re.test(els[i].className))a.push(els[i]);
    return a;
}
var tabs = document.getElementsByClassName(document.body,'tab');

但是IE仍然显示:

对象不支持此属性或方法

有什么想法,更好的方法,错误修复?

我希望不要使用任何涉及jQuery或其他“笨重JavaScript”的解决方案。

更新:

我让它工作了!

正如@joe提到的那样,该函数不是document的方法。

因此,工作代码看起来像这样:

function getElementsByClassName(node, classname) {
    var a = [];
    var re = new RegExp('(^| )'+classname+'( |$)');
    var els = node.getElementsByTagName("*");
    for(var i=0,j=els.length; i<j; i++)
        if(re.test(els[i].className))a.push(els[i]);
    return a;
}
var tabs = getElementsByClassName(document.body,'tab');


如果你只需要支持IE8+,那么这个就可以工作:

if(!document.getElementsByClassName) {
    document.getElementsByClassName = function(className) {
        return this.querySelectorAll("." + className);
    };
    Element.prototype.getElementsByClassName = document.getElementsByClassName;
}

像正常使用一样使用它:

var tabs = document.getElementsByClassName('tab');

2
你确定你的更新是正确的吗?它不应该是 var tabs = getElementsByClassName(document.body,'tab') 吗?注意我删掉了 document.getElement...。 - Anthony
你最后的示例正确吗?你正在传入“.tab”,但由于你的内部方法添加了点号,因此它不应该带有点号,如果是IE9+,则也没有点号,是吗?这不应该是var tabs = document.getElementsByClassName('tab');吗?为什么在最后两个示例中都有一个“或”?我一定是漏掉了什么。 - BoBoCoding
@BoBoCoding 已修复。 - Web_Designer
请参阅 Polyfill for getElementsByClassName for particular uses 中的 getElementsByClassName polyfill gist - Vadzim
@Web_Designer -- 如果我的先前评论有帮助,能否给我点个赞?因为“声望”确实可以在Stack Overflow上开启不同的功能。谢谢。 - BoBoCoding
7个回答

56

它不是文档的一个方法:

function getElementsByClassName(node, classname) {
    var a = [];
    var re = new RegExp('(^| )'+classname+'( |$)');
    var els = node.getElementsByTagName("*");
    for(var i=0,j=els.length; i<j; i++)
        if(re.test(els[i].className))a.push(els[i]);
    return a;
}

tabs = getElementsByClassName(document.body,'tab');  // no document

你是在 jsFiddle.net 上测试还是在自己的服务器/机器上测试? - Joe
@好奇的网络开发者 - 您现在遇到了什么错误?它是在哪一行中断的? - nnnnnn

18

你可以为较旧的浏览器创建该函数

if (typeof document.getElementsByClassName!='function') {
    document.getElementsByClassName = function() {
        var elms = document.getElementsByTagName('*');
        var ei = new Array();
        for (i=0;i<elms.length;i++) {
            if (elms[i].getAttribute('class')) {
                ecl = elms[i].getAttribute('class').split(' ');
                for (j=0;j<ecl.length;j++) {
                    if (ecl[j].toLowerCase() == arguments[0].toLowerCase()) {
                        ei.push(elms[i]);
                    }
                }
            } else if (elms[i].className) {
                ecl = elms[i].className.split(' ');
                for (j=0;j<ecl.length;j++) {
                    if (ecl[j].toLowerCase() == arguments[0].toLowerCase()) {
                        ei.push(elms[i]);
                    }
                }
            }
        }
        return ei;
    }
}

14
function getElementsByClassName(className) {
if (document.getElementsByClassName) { 
  return document.getElementsByClassName(className); }
else { return document.querySelectorAll('.' + className); } }

我很确定这与Leonid的函数相同,但当它可以时,它使用document.getElementsByClassName


10

你无法完全复制 getElementsByClassName,因为它返回一个节点列表(nodeList),所以它的值是实时的,并且会随着文档更新。

你可以返回一个静态数组,其中包含共享相同类名的元素 - 但它不会“知道”文档何时更改。

(这样的事情发生过多,会使库看起来紧凑...)

function getArrayByClassNames(classes, pa){
    if(!pa) pa= document;
    var C= [], G;
    if(pa.getElementsByClassName){
        G= pa.getElementsByClassName(classes);
        for(var i= 0, L= G.length; i<L; i++){
            C[i]= G[i];
        }
    }
    else{
        classes= classes.split(/\s+/);
        var who, cL= classes.length,
        cn, G= pa.getElementsByTagName('*'), L= G.length;
        for(var i= 0; i<cL; i++){
            classes[i]= RegExp('\\b'+classes[i]+'\\b');
        }
        classnameLoop:
        while(L){
            who= G[--L];
            cn= who.className;
            if(cn){
                for(var i= 0; i<cL; i++){
                    if(classes[i].test(cn)== false) {
                        continue classnameLoop;
                    }
                }
                C.push(who);
            }
        }
    }
    return C;
}

//示例

var A = getArrayByClassNames('sideBar local')


3
+1 表示注意到与真正的 getElementsByClassName 不同的是,兼容性补丁(包括 querySelectorAll)提供的是静态快照,而不是动态集合。 - Søren Løvborg

9

IE8:

document.getElementsByClassName = function (className) {
    return document.querySelectorAll('.' + className)
}

1
在我的IE9上无法工作:“对象不支持属性或方法'querySelectorAll'” - Phil

0
function _getClass(whatEverClasNameYouWant){
var a=document.getElementsByTagName('*');
   for(b in a){
      if((' '+a[b].className+' ').indexOf(' '+whatEverClasNameYouWant+' ')>-1){
      return a[b];
      }
   }
}

0
我只想改进IE8的querySelectorAll回退。就像其他人回答的那样,简单的方法是将该函数添加到Element.prototype中。
this.querySelectorAll('.' + className);

但是还存在一些问题:

  • 它不能处理未修剪的字符串(在开头)。
  • 它不能处理多个类。
  • 它不能处理“奇怪”的类字符(/$*等)
  • 它不能处理以数字开头的类(无效标识符)

这意味着需要进行一些“修复”,例如:

"abcd"     ->  ".abcd"
"a   b cd" ->  ".a.b.cd"
"   a b  " ->  ".a.b  "
"a/b$c d"  ->  ".a\/b\$c.d"
"1234"     ->  ".\000031234"

代码:

this.querySelectorAll(className
    .replace(/(?=[^ \w])/g, '\\')   // Escape non-word characters
    .replace(/\b\d/g, '\\00003$&')  // Escape digits at the beginning
    .replace(/(^| +)(?!$| )/g, '.') // Add "." before classes, removing spaces
);

1
该函数也可以添加到HTMLDocument.prototype中,以便在document中使用它。 - Oriol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接