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.
書式
elements = document.getElementsByTagName(name)
* elements は、ツリーに現れる順に並べられた、見つかった要素の生きた NodeList です。
* name は要素の名前を表す文字列です。特殊な文字列 "*" は全ての要素を表します。
これに気がつくまで、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の仕様を読んでおこうと思いました。