データに応じて要素を生成するときのスマートな方法
サーバーから受け取ったデータに応じて、要素を生成したいということはよくある。
テンプレートエンジンなどを使うと、そのデータを綺麗に処理することができる。 けど、それを使わずに素のJSで書くのはなかなかめんどくさい。
たとえば、こんなデータがあるとする。
var users = [ { name: 'tom', age: 18 }, { name: 'bob', age: 24 }, { name: 'john', age: 36 } ];
このデータから、以下のHTMLを生成する方法を考える。
<dl> <dt>name</dt> <dd>tom</dd> <dt>age</dt> <dd>18</dd> </dl> <dl> <dt>name</dt> <dd>bob</dd> <dt>age</dt> <dd>24</dd> </dl> <dl> <dt>name</dt> <dd>john</dd> <dt>age</dt> <dd>36</dd> </dl>
まず、普通に書くとこんな感じだと思う。
var frag = document.createDocumentFragment(); for (var i = 0, l = users.length; i < l; i++) { var user = users[i]; var dl = document.createElement('dl'); var nameTerm = document.createElement('dt'); nameTerm.textContent = 'name'; dl.appendChild(nameTerm); var nameDesc = document.createElement('dd'); nameDesc.textContent = user.name; dl.appendChild(nameDesc); var ageTerm = document.createElement('dt'); ageTerm.textContent = 'age'; dl.appendChild(ageTerm); var ageDesc = document.createElement('dd'); ageDesc.textContent = user.age; dl.appendChild(ageDesc); frag.appendChild(dl); } var result = document.getElementById('result'); result.appendChild(frag);
書くコードが無駄に多くて、わかりにくくてあまりスマートでない。 これを、わかりやすく書いてみる。
var html = users.map(function (user) { return ` <dl> <dt>name</dt> <dd>${user.name}</dd> <dt>age</dt> <dd>${user.age}</dd> </dl> `; }); var result = document.getElementById('result'); result.insertAdjacentHTML('beforeend', html.join(''));
最初の方法みたいに、document.createElementを実行するとNodeListが返るので、個々に別の要素に追加していかないといけない。
それを、配列の中にStringとして生成すると、実際にDOMに追加するタイミングで全て連結してそのままHTMLにパースできる。
insertAdjacentHTMLの第一引数にbeforeendを指定すると要素の末尾に、afterbeginを指定すると要素の先頭に第二引数のHTMLが追加される。
要素の中身を空にしてから追加したいというときは、innerHTMLで追加してもいいけど、速度的に微妙なのでwhile文とかで除去してからinsertAdjacentHTMLで追加するのがいいと思う。
var empty = function (el) { while (el.firstChild) { el.removeChild(el.firstChild); } }; var container = document.getElementById('container'); empty(container);