• document.getElementsByClassName的理想实现(@司徒正美 大神)


    不知道怎地,突然不能收藏了,只好把司徒正美的这篇文章给copy过来了,建议大家看原版:http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1529640.html

    各种实现方式

    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;
    }

    当然如果游览器原生支持,就用原生的。

    各主流游览器的支持情况
    IE8IE7IE6FF3FF2Saf3Op9Op10Chrome
    N N N Y N Y Y Y Y

    速度比较一览

    综合以上方案,我得出了一个最理想的实现——兼容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)
            }else{
              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;
        }
      }
  • 相关阅读:
    promethus监控JVM jar包
    ubuntu中文乱码解决办法
    IT焦躁中的赤子青年
    ftp neo4j http kafka搭建
    查看python脚本执行过程
    解决coredns-7f9c544f75-2sjs2一直处于ContainerCreating
    番茄工作法
    数据库的性能优化
    MyBatis
    CentOS下安装JDK,Tomcat,Redis,Mysql,及项目发布
  • 原文地址:https://www.cnblogs.com/Mygirl/p/2804477.html
Copyright © 2020-2023  润新知