1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【JavaScript】fragmentを親NodeにappendChild()したら中身が消えた

Last updated at Posted at 2024-09-12

事象

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】
image.png

childrenプロパティを参照してみる。

index.js
console.log(Tr3.GetFragment().children);
// -> HTMLCollection[tr]
Table.appendChild(Tr3.GetFragment());
console.log(Tr3.GetFragment().children);
// -> HTMLCollection[]

確かにappendChild()を実行すると、trが消えている。

調査3

公式ドキュメントを確認。
【appendChild()】
image.png
し、知らなかった。

コーディングの修正

上記を踏まえた上で、

  • 要素を描画するタイミングで、インスタンスのプロパティ要素の中身が消えることに違和感がある
  • そこを意識しないで済むように修正
  • 汎用的になるよう、コンストラクタ部分を切り出し
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の為、余計にややこしくなってる感じがある

補足

fragmentのドキュメントにも記載ありました。
image.png
まぁ、実体験しないと文章の意味を理解できなかった気がします。

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?