Closed6

shawdow DOM / Web Component とは

ebi_yuebi_yu

https://developer.mozilla.org/ja/docs/Web/API/Web_components/Using_shadow_DOM

軽く読んだ感じ

  • HTMLのコードをカプセル化して、他のHTMLと分離したもの
  • shadow化することで、shawdow DOM外で定義したcssの影響を受けなくなる。

jsでは特定のHTMLエレメントにアタッチして、shadow DOMを作れる。

const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);

HTMLでは<template>タグで囲む

<div id="host">
  <template shadowrootmode="open">
    <span>I'm in the shadow DOM</span>
  </template>
</div>
ebi_yuebi_yu

shadow DOMの使い方

パターン1 : js内で作った要素をアタッチ

  • shadow DOMのスイッチをONにして、js内で作った要素をアタッチ
  • shadow DOMスイッチをONにすると、元々の要素は表示されなくなる。
  • shadow DOM内で設定したcssはshadow DOMの外の要素には影響を及ぼさない(scoped CSSになっている)
<p>shadowCSSが適用されない</p>
<div id="my-test">
    <p>Test</p>
</div>
<script>
    const testHost = document.getElementById("my-test")

    // shadow DOMをアタッチ
    const testHostShadow = testHost.attachShadow({ mode: "open" });

    // shadow DOMにstyleをアタッチ
    const p = document.createElement("p");
    const style = document.createElement("style");
    style.textContent = "p { color: red; }";

    // shadow DOMにshadow DOMをセットする。
    testHostShadow.appendChild(style);
    p.innerHTML = "<p>上書きされた</p>";
    p.style = "p { color:red; }"
    testHostShadow.appendChild(p);
</script>

表示するとこんな感じ
<p>Test</p>は上書きされたので表示されない。

パターン2 : template要素をアタッチ

  • template要素は書くだけでは実際には画面上に表示されない
  • template.content.cloneNode(true)みたいにすると表示される。
  • template要素を定義しておいて、それをshadow DOMにアタッチするときにtemplate.content.cloneNode(true)にする。
  • shadow DOMで上書きされる要素内 の子要素で、slot属性を指定した要素は、テンプレート内で同じ名前を持つ <slot> 要素に置換される。
<template id="my-template">
    <style>
        p {
            font-weight: bold;
        }
    </style>
    <slot name="message"></slot>
    <p>shadow DOM で上書きした要素</p>
</template>
<div id="my-host">
    <p>shadow DOMに上書きされて表示されないようそう</p>
    <p slot="message">Slotが差し込まれる</p>
</div>
</body>
<script>
    // shadow DOM を使ってtemplate要素をアタッチ
    const myHost = document.getElementById("my-host");
    const myHostShadow = myHost.attachShadow({ mode: "open" });
    const template = document.getElementById("my-template");
    myHostShadow.appendChild(template.content.cloneNode(true));
</script>
<style>
    p {
        text-decoration: underline;
    }
</style>

表示するとこんな感じ
shadow DOMで上書きされる要素内 の子要素で、slot属性を指定した要素が差し込まれている。

ebi_yuebi_yu

カスタムエレメント

  • カスタムエレメントの定義において<template>とshadow DOMを使うことが多いらしい
  • 確かに、カスタムエレメントの定義にすごく便利そう
<style>
    my-song-list {
        display: flex;
        gap: .5rem;
    }

    my-song {
        border: 1px solid #999;
        padding: .2rem;
        width: 12rem;
    }
</style>

<template id="my-template">
    <style>
        .title {
            font-weight: bold;
        }
    </style>
    <div class="title">
        <slot name="title"></slot>
    </div>
    <div class="artist">(By <slot name="artist"></slot>)</div>
</template>

<my-song-list>
    <my-song>
        <span slot="title">Let it be</span>
        <span slot="artist">The Beatles</span>
    </my-song>
    <my-song>
        <span slot="title">Hotel California</span>
        <span slot="artist">The Eagles</span>
    </my-song>
    <my-song>
        <span slot="title">Stairway to Heaven</span>
        <span slot="artist">Led Zeppelin</span>
    </my-song>
</my-song-list>

<script>
    const template = document.getElementById("my-template");
    customElements.define("my-song",
        class MySong extends HTMLElement {
            constructor() {
                super();
                const shadowRoot = this.attachShadow({ mode: "open" });
                shadowRoot.appendChild(template.content.cloneNode(true));
            }
        }
    );
</script>

表示するとこんな感じ

ebi_yuebi_yu

感想

生のHTMLでも結構Vueなどのフロントエンドフレームワークっぽいことができるんだなーという感じ。
フレームワーク使う必要ない時はこれを使ってみると良いかもしれない。

このスクラップは26日前にクローズされました