事象
tbodyのtrをクラス化し、インスタンスから操作しようとしたら
描画後にエラーとなった為、挙動を調べた。
index.html
<table>
<thead>
<tr>
<th class="UserNo">社員No</th>
<th class="UserName">社員名</th>
</tr>
</thead>
<template id="TrTemplate">
<tr>
<td class="UserNo"></td>
<td class="UserName"></td>
</tr>
</template>
<tbody>
<!-- jsで生成 -->
</tbody>
</table>
MyTr.js
class MyTr{
// private
#Tr;
constructor(UserNo, UserName){
// templateをfragment化してプロパティに保存
this.#Tr = document.querySelector(`#TrTemplate`).content.cloneNode(true);
this.SetUserNo(UserNo);
this.SetUserName(UserName);
}
GetFragment(){
return this.#Tr;
}
SetUserNo(UserNo){
this.#Tr.querySelector(`.UserNo`).textContent = UserNo;
}
SetUserName(UserName){
this.#Tr.querySelector(`.UserName`).textContent = UserName;
}
}
index.js
// インスタンス生成
const Tr1 = new MyTr(1, '鈴木');
const Tr2 = new MyTr(2, '田中');
const Tr3 = new MyTr(3, '佐藤');
// ノードを追加
const Tbody = document.querySelector(`table tbody`);
Tbody.appendChild(Tr1.GetFragment());
Tbody.appendChild(Tr2.GetFragment());
Tbody.appendChild(Tr3.GetFragment());
// 佐藤 ⇒ 渡辺へ書換
Tr3.SetUserName('渡辺');
// -> Uncaught TypeError: Cannot set properties of null (setting 'textContent')
調査1
#Trプロパティの中身がnullになっているのでエラーが出ている模様。
appendChild()の前後にログを挟んでみる。
index.js
console.log(Tr3.GetFragment());
// -> #document-fragment
Table.appendChild(Tr3.GetFragment());
console.log(Tr3.GetFragment());
// -> #document-fragment
fragmentはあるようだが、中身がわからない。
調査2
公式ドキュメントを探すと良さそうなプロパティを発見。
【DocumentFragment】
childrenプロパティを参照してみる。
index.js
console.log(Tr3.GetFragment().children);
// -> HTMLCollection[tr]
Table.appendChild(Tr3.GetFragment());
console.log(Tr3.GetFragment().children);
// -> HTMLCollection[]
確かにappendChild()を実行すると、trが消えている。
調査3
公式ドキュメントを確認。
【appendChild()】
し、知らなかった。
コーディングの修正
上記を踏まえた上で、
- 要素を描画するタイミングで、インスタンスのプロパティ要素の中身が消えることに違和感がある
- そこを意識しないで済むように修正
- 汎用的になるよう、コンストラクタ部分を切り出し
MyCloneNode.js
class MyCloneNode{
// protected
_Element;
constructor(ParentNode, CssSelector){
const CloneNode = document.querySelector(CssSelector).content.cloneNode(true);
// クローン内の最初の要素をプロパティに設定
this._Element = CloneNode.firstElementChild;
// 親ノードに追加
ParentNode.appendChild(this._Element);
}
}
MyTr.js
class MyTr extends MyCloneNode{
constructor(ParentNode){
super(ParentNode, `#TrTemplate`);
}
SetUserNo(UserNo){
this._Element.querySelector(`.UserNo`).textContent = UserNo;
}
SetUserName(UserName){
this._Element.querySelector(`.UserName`).textContent = UserName;
}
}
index.js
const Tbody = document.querySelector(`table tbody`);
// インスタンス生成
const Tr1 = new MyTr(Tbody);
const Tr2 = new MyTr(Tbody);
const Tr3 = new MyTr(Tbody);
Tr1.SetUserNo(1);
Tr1.SetUserName('鈴木');
Tr2.SetUserNo(2);
Tr2.SetUserName('田中');
Tr3.SetUserNo(3);
Tr3.SetUserName('佐藤');
Tr3.SetUserName('渡辺');
最後に
関数ベースでコーディングしてきた為、オブジェクト指向の考えが中々ピンとこないが、
- 元々のコーディングではtrのappendを上位側が行っていたが、クラスの責任範囲としては誤っていた?
- 環境がPHP、HTML、CSS、JavaScriptの為、余計にややこしくなってる感じがある