2007-11-08

XPath/DOM : Class属性による要素抽出 - クロスブラウザ document.getElementsByClassName

javascriptの優れたライブラリが近年続々と公開されています。

幸いな事にprototype.jsjQuery、そして最近ではさらにamachangさんによるライブラリによってjavascriptによるDOM要素抽出が気軽に見通しよく記述することができる環境が整っているようです。

車輪の再実装ですがタグ名とクラス名を指定してDOM要素抽出を行なうという用途のための小さなコードを書きました。既存ライブラリを使わない小さなサイズのスクリプトでちょっとしたDOM操作のために。

使い方はgetBoxes(klass, tag) でクラス属性値klass、タグ名tagのDOM要素を配列にして抽出します。tagを省略すると、指定されたクラス属性値を持つ全てのタグを抽出します。 たとえば、getBoxes('test', 'div') では、testをクラス属性値に持つタグ名divの要素を配列で返します。

■追記:2007/11/12
getBoxes(klass, tag, context) のように第3引数にDOM要素を指定すると、その要素と子孫要素が探索対象範囲となる機能を追加しました。第3引数を指定しなければページ内の全要素が探索対象となります。
XPathを使用する場合、XPath式とnamespace resolver(ここでは、nullです)から生成されるオブジェクトを保存し再利用するようにしました。

var getBoxes
    = (/Firefox\/(\d+)/.test(navigator.userAgent) && RegExp.$1 > 2) ?
          (function(){
              var toChangeCase = (document.documentElement.nodeName == 'HTML')
                               ? 'toUpperCase' : 'toLowerCase';
              return function(klass, tag, context){
                  var boxes = (context || document).getElementsByClassName(klass);
                  return (tag && (tag = tag[toChangeCase]()))
                       ? Array.filter(boxes, function(e){return e.nodeName == tag;})
                       : boxes;
              };
          })()
    : (document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('XPath', '3.0')) ?
          (function(){
              var toChangeCase = (document.evaluate('//*[local-name()="HTML"]', document, null, XPathResult.BOOLEAN_TYPE, null).booleanValue)
                               ? 'toUpperCase' : 'toLowerCase',
                  cache = {};
              return function(klass, tag, context){
                  var boxes = [],
                      key   = (tag || '*') + ':' + klass,
                      exp   = cache[key] || 
                             (cache[key] = document.createExpression('//*'
                            + (tag ? ('[local-name()="' + tag[toChangeCase]() + '"]') : '')
                            + '[contains(concat(" ",@class," ")," ' + klass + ' ")]', null)),
                      nodes = exp.evaluate(context || document, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null),
                      node,
                      i = 0;
                  if(nodes)
                      while(node = nodes.iterateNext())
                          boxes[i++] = node;
                  return boxes;
              };
          })()
    :     function(klass, tag, context){
              var boxes = [],
                  regex = new RegExp('(?:^|\\s)' + klass + '(?:\\s|$)'),
                  i = 0;
              /*@cc_on
              if(@_jscript_version > 5.5 || @_mac || tag || !context){
                @*/
              var nodes = /*@cc_on (@_jscript_version < 5.6 && !@_mac && !tag && !context) ? document.all : @*/
                          (context || document).getElementsByTagName(tag && tag.toLowerCase() || '*'),
                  node,
                  j = 0;
              if(nodes)
                  while(node = nodes[i++])
                      if(regex.test(node.className))
                          boxes[j++] = node;
              /*@cc_on
              }else{
                  (function walk(e){
                      if(e.nodeType == 1 && regex.test(e.className)) boxes[i++] = e;
                      e = e.firstChild;
                      while(e){
                          walk(e);
                          e = e.nextSibling;
                      };
                  })(context);
              };@*/
              return boxes;
          };


資料

0 件のコメント: