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

mac2022-06-30  73

不知道怎地,突然不能收藏了,只好把司徒正美的这篇文章给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; }

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

各主流游览器的支持情况IE8IE7IE6FF3FF2Saf3Op9Op10ChromeNNNYNYYYY

速度比较一览

综合以上方案,我得出了一个最理想的实现——兼容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;      }    }

转载于:https://www.cnblogs.com/Mygirl/archive/2012/12/06/2804477.html

最新回复(0)