hagino3000's blog

平成アーカイブス (更新停止)

getElementsByTagNameの結果のNodeListはソートされていることに気づいた

getElementsByTagName

Returns a NodeList of all the Elements with a given tag name in the order in which they are encountered in a preorder traversal of the Document tree.

Document Object Model Core

書式


elements = document.getElementsByTagName(name)


* elements は、ツリーに現れる順に並べられた、見つかった要素の生きた NodeList です。
* name は要素の名前を表す文字列です。特殊な文字列 "*" は全ての要素を表します。

document.getElementsByTagName - MDC

これに気がつくまで、getElementsByTagNameの結果のNodeListを使って子から親に向かって要素の切り替えを行う処理なんかは再帰を使って実装していたんだけど、全くその必要がない事が判明。

font要素をspan要素に切り替えるサンプル

再帰を使って、treeの末端側から根元に向かって処理するパターン
var nodeList = rootElm.getElementsByTagName('font');

// nodeListは生きたリストなので、処理が終わった時点でlengthが0
while(nodeList.length > 0){

  (function processNode(node){

    for(var i=0; i<node.childNodes.length; i++)
      arguments.callee(node.childNodes[i]); //子要素を先に処理

    // exchange element
    if(node.nodeName == 'font'){
      var spanElm = document.createElement('span');
      spanElm.style.fontFamily = node.face;

      // 子ノードの詰め替え
      while(node.hasChildNodes())
        spanElm.appendChild(node.removeChild(node.firstChild));

      node.parentNode.insertBefore(spanElm, node);
      node.parentNode.removeChild(node);
    }
  })(nodeList[0]);
  
}
再帰を使わないパターン
var nodeList = rootElm.getElementsByTagName('font');

// nodeListは生きたリストなので、処理が終わった時点でlengthが0
while(nodeList.length > 0){

  // nodeListの最後から処理すると必ず子から親に向かって処理できる
  var fontElm = nodeList[nodeList.length - 1];

  // exchange element
  var spanElm = document.createElement('span');
  spanElm.style.fontFamily = fontElm.face;

  // 子ノードの詰め替え
  while(fontElm.hasChildNodes())
    spanElm.appendChild(fontElm.removeChild(fontElm.firstChild));

  fontElm.parentNode.insertBefore(spanElm, fontElm);
  fontElm.parentNode.removeChild(fontElm);
}

というわけで

ちゃんとDOMの仕様を読んでおこうと思いました。