HTML文書内の見出しを抽出し、OL要素で階層化する
(5). HTML 4.01 文書内の見出し(H1-H6 要素)を抽出し、OL 要素で階層化せよ。
<ol> <li>H1-1 <ol> <li>H2-1 <ol> <li>H3-1</li> <li>H3-2</li> </ol> </li> <li>H2-2 </ol> <li>H1-2 <ol> <li>H2-1 <ol> <li>H3-1</li> <li>H3-2</li> </ol> </li> </ol> </li> </ol>
数日に渡り何時間か考えて、ようやく自分なりの答えを出せたので書き残しておく。
function createTOC() { var d = document; var elems = document.getElementsByTagName('*'); var ol = d.createElement('OL'); var lv = 1; var cnt = [null,1,1,1,1,1,1]; var ft = true; var li, ol_, li_, lv_, j, ol__, li__, li$, up, down; for(var i=0, l=elems.length; i<l; i++) { elem = elems.item(i); if(!/H[1-6]/.test(elem.tagName.toUpperCase())) continue; lv_ = elem.tagName.slice(1); li_ = d.createElement('LI'); if(lv >= lv_) cnt[lv_]++; // 上がるか同じ階層のとき++ else cnt[lv_] = 1; // 下がるときリセット li_.appendChild(d.createTextNode('H' + lv_ + '-' + cnt[lv_] + ' ' + getText(elem))); if(lv == lv_) { // 同じ階層 li ? li.parentNode.appendChild(li_) : ol.appendChild(li_); } else if(lv < lv_) { // 下の階層へ down = lv_ - lv; ol_ = d.createElement('OL'); if(down == 1) { ol_.appendChild(li_); } else { // 階層が飛ぶとき for(j=0; j<down-1; j++) { // (li__ > ol__ > li$)を繰り返す li__ = d.createElement('LI'); ol__ = d.createElement('OL'); li__.appendChild(ol__); if(ft) { // 初回だけ ol__.appendChild(li_); ft = false; } else { ol__.appendChild(li$); } li$ = li__; // li$を頭にして階層化していく } ol_.appendChild(li$); ft = true; } li.appendChild(ol_); } else { // 上の階層に戻る up = 1 + 2 * (lv - lv_); // liの親OL + (OLの親LIの親OL) * 階層分繰り返す for(j=0; j<up; j++) { li = li.parentNode; // 便宜的に変数liを使う } li.appendChild(li_); } li = li_; lv = lv_; } return ol; } function getText(elem) { if(!elem) return; var text = ""; var node = elem.firstChild; while(node) { if(node.nodeType == 3) text += node.nodeValue; else if(node.nodeType == 1 && node.hasChildNodes()) text += getText(node); node = node.nextSibling; } return text; } document.body.insertBefore(createTOC(), document.body.firstChild);
動作に関しては問題ないと思う。見出しレベルが飛んでいるとき(H1からH5とか、H4からH2とか)でもレベルに沿った階層化がされるはず。
模範解答は理解できなかった。いつか読み解きたい。