JavaScriptで文字列をDOM要素に変換する
プレーンのJavaScript
でHTML
の文字列をDOM要素に変換する方法。
jQuery使えばいいのだが……
関数にしたかったので、こういうのを書いてみました。引数のHTML文字列は文法エラーが無いこと前提です。
/**
* HTMLの文字列をNodeListに変換して返却
* @param String HTMLの文字列
* @return NodeList
*/
var stringToDOMElements = (function() {
// 親にしてよい要素
var parentTagNames = {
title: 'head', base: 'head', link: 'head', meta: 'head', style: 'head',
li: 'ul', dt: 'dl', dd: 'dl', figcaption: 'figure', rb: 'ruby', rt: 'ruby', rtc: 'ruby', rp: 'ruby',
param: 'object', track: 'audio', area: 'map', caption: 'table', colgroup: 'table', col: 'colgroup',
thead: 'table', tbody: 'table', tfoot: 'table', tr: 'tbody', th: 'tr', td: 'tr',
optgroup: 'select', option: 'select', legend: 'fieldset',
};
return function(str) {
str = str.replace(/[\r\n\t]/g, ''); // 改行コードとタブを全て除去
// 先頭のタグ名に応じて親要素作成
var parentTagName = 'div';
var m = str.match(/^<([a-zA-Z]+)[ >]/);
if (m !== null && m[1] !== undefined && parentTagNames[m[1]] !== undefined) {
parentTagName = parentTagNames[m[1]];
}
var parentEl = document.createElement(parentTagName);
// 親要素のinnerHTMLに突っ込んでDOM要素に変換
parentEl.innerHTML = str;
return parentEl.childNodes;
};
})();
使用例
// 使用例その1
var result1 = stringToDOMElements(`<ul>
<li>ほにゃ</li>
<li>ふにゃ</li>
</ul>
<div>へにゃ</div>`);
console.log(result1[0].firstElementChild.innerHTML); // ほにゃ
console.log(result1[1].innerHTML); // へにゃ
// 使用例その2
var result2 = stringToDOMElements(`<tr>
<td>データA-1</td>
<td>データB-1</td>
</tr>
<tr>
<td><span class="aiai ueue">データA-2</span></td>
<td>データB-2</td>
</tr>`);
console.log(result2[0].childNodes[1].innerHTML); // データB-1
console.log(result2[1].childNodes[0].childNodes[0].innerHTML); // データA-2
console.log(result2[1].childNodes[0].childNodes[0].classList[1]); // ueue
備考
やや変な感じになってしまったので、以下経緯。
- 最初に改行コードとタブ文字を除去してるのは、文字列状態での要素間の改行コードやタブが、最後に
childNodes
で取得した時にテキストノード扱いされていて邪魔だったから。 innerHTML
に突っ込むために一時的に生成している親要素は、文字列の先頭の要素に応じて切り替えてる。div
一辺倒だと、ブラウザによっては (?) 文法エラーになって無視されてしまうから。
2つ目の件はChromeでしか確認してません。Chromeだと div
の子供にいきなり tr
要素を突っ込んだり、同様に div
の子供に tbody
要素を突っ込んだ時に無視されて、剥き出しのテキストノードしか残りませんでした。でもほとんどの場合はそうはならなかったので、ここまで細かく親要素を切り替えなくても大丈夫だとは思います。
各要素に対してどういう親要素を設定できるかは、一応 HTML5.2 の仕様書 を見て確認しました。