10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

shadow DOM」ってなに?特徴と使い方

Last updated at Posted at 2023-04-22

「shadowDOM」ってなんだ?どうやって使うんだ?のヒントにしてもらえると幸いです。

【この記事を読んでほしい人】
・shadowDOMについて何もしらない人
・shadowDOMの使い方について知りたい人

shadowDOMってなに?

MDNから引用した文章は下記です。

ウェブコンポーネントにおける重要な側面の一つが、カプセル化です。マークアップ構造、スタイル、動作を隠蔽し、コード上の他のコードから分離することで、他の部分でクラッシュすることを防ぎ、コードをきれいにしておくことができます。シャドウ DOM API はこの主要部分であり、隠蔽され分離された DOM を要素に取り付けるための方法を提供しています。この記事ではシャドウ DOM を使う基本を記述しています。

通常のDOMを「Light DOM」と呼び、見えているDOMから分離されたものを「Shadow DOM」と言われています。

わかりそうでわからない…。

ここからわかったことは

  • 何やら表に出ているコードと分離でき、影響を分けられるらしい
  • shadowDOOM APIというものが用意されている

shadowDOMは、Edge75以降、Firefox63以降、Chrome、Opera、 Safariが対応しているようです。

shadowDOMはどんな特徴がある?

shadowDOMには下記のような特徴があります。

  • shadowDOMにあるNodeにはJavaScriptのquerySelector()ではアクセスできない
  • shadowDOM内のCSSはscopedCSSでLightDOMに影響を与えない(逆にLightDOMのCSSはshadowDOMに影響を与えない)

注意点としてCSSの継承は分離できません。
shadowDOMであっても親で指定されたstyleは継承されます…。

shadowDOMはどんな時に使うの?

CSSをscopeを切って指定したい時に利用するのだと思っています。

それ以外の具体的な使い方はわかってませんが、便利な使い方があるかもしれません!

shadowDOMの作り方・使い方

任意の要素に対して.attachShadow({mode: 'open'});を使用する。
modeをclosedで設定するとアクセスできなくなります。

const shadow = this.attachShadow({mode: 'open'});
const shadow = this.attachShadow({mode: 'closed'});

JavaScriptからアクセスできるようになる。

const shadowDom = element.shadowRoot;

具体的な記述例

例えばshadowDOMでpタグを作りたい場合の例を紹介します。

<body>
  <div id="shadow">ここにリード文が入ります</div>
  <p>ここにリード1文が入ります</p>
</body>
body {
  font-size: 10px;
}

p {
  color: red;
}
const shadow = document.getElementById('shadow');
const shadowRoot = shadow.attachShadow({ mode: 'open' });

const p = document.createElement('p');
p.textContent = 'リード文2のテキストが入ります。';

shadowRoot.appendChild(p);

image.png

実際にやってみてわかったのですが、shadowDOMとして指定した要素の中身は表示されなくなります。
今回の場合だと「ここにリード文が入ります」というテキストはブラウザには表示されませんでした。

ちなみにどんなに要素が入っていてもshadowDOMとして.attachShadow({ mode: 'open' });をするとブラウザには表示されなくなるようです。

先ほどのコードを下記のようにしても…

<body>
  <div id="shadow">
    <h1>h1テキストが入る</h1>
    <p>ここにリード文が入ります</p>
    <ul>
      <li>list-item01</li>
      <li>list-item02</li>
    </ul>
  </div>
  <p>ここにリード1文が入ります</p>
</body>

結果は先ほどと変わりません。
image.png

このままだとshadowDOMを実装するために表示されないHTMLタグを記述することになります…。
ここで使えるのが<slot>です!
<slot>の利用方法については、<slot>を使うをご覧ください。

shadowDOM作れる要素、作れない要素

公式にも書いてありますが、引用しておきます。
一部のHTMLタグに対してのみ作成できるので注意ください!

thisの ローカル名が以下のいずれかに該当しない場合:
有効なカスタム要素名
"article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p","section", or "span"
とすると、"NotSupportedError"DOMExceptionを投げる。

独自のshadowDOMを作る

独自でコンポーネントを作ることも可能です。

customElements.define(
  'original-div',
  class extends HTMLElement {
    constructor() {
      super();

      const shadowRoot = this.attachShadow({ mode: 'open' });
      shadowRoot.innerHTML = `
      <style>p { color: green;}</style>
      <div>
        <p>リード文3のテキストが入ります。</p>
      </div>
  `;
    }
  }
);

HTMLは下記のように記述するとブラウザに表示されます。

<original-div />

image.png

閉じタグの「/」を省略しても表示はされるようですが、個人的には閉じられていることがわかるように「/」は記載しておいた方がいいかと思います!

開発者ツールで確認したところ、ブラウザ上では下記のように表示されています。
image.png

独自のshadowDOMを作る際は一部命名が利用できないので注意してください!

name must not be any of the following:
annotation-xml
color-profile
font-face
font-face-src
font-face-uri
font-face-format
font-face-name
missing-glyph

name must not be any of the following

実際に「color-profile」でshadowDOMを作って表示使用とするとコンソールにエラーが出ます。
image.png

<slot>を使う

<slot>タグを使うことですでに書かれた記述されたHTMLとshadowDOMを合わせて表示できます!

HTMLを下記のように修正します。

<div id="shadow">
  <h1 slot="heading">h1テキストが入る</h1>
  <p slot="paragraph">ここにリード文が入ります</p>
  <ul slot="list">
    <li>list-item01</li>
    <li>list-item02</li>
  </ul>
</div>
<p>ここにリード1文が入ります</p>
<original-div />

現時点ではJavaScriptに変更を加えていないので表示に変化なく、id="shadowDOM"の中身は表示されてません。
image.png
JavaScript側のcustomElementsで定義した中を編集します!

customElements.define(
  'original-div',
  class extends HTMLElement {
    constructor() {
      super();

      const shadowRoot = this.attachShadow({ mode: 'open' });
      shadowRoot.innerHTML = `
      <style>p { color: green;}</style>
      <div>
        <slot name="heading"><h1>h1テキストが入る</h1></slot>
        <slot name="paragraph"><p>ここにリード文が入ります</p></slot>
        <slot name="list">
          <ul>
            <li>list-item01</li>
            <li>list-item02</li>
          </ul>
        </slot>
        <p>リード文3のテキストが入ります。</p>
      </div>
  `;
    }
  }
);

<slot>タグを記述してname属性にHTML側のslot属性と対応するものを記載していきます。
すると表示が下記のようになります。
image.png
<div id="shadow">の中で指定した<h1><ul>が表示されました。

【まとめ】HTMLの「shadow DOM」について

shadowDOMを勉強したきっかけは、CSSをスコープを切って定義できるものがあると知ったためです。
現在の開発環境でどうしてもCSSをスコープを切っていかないといけない状況下での選択肢としてshadowDOMもありだなと感じました。

shadowDOMを知ることで自分の知識の浅はかさを体感したので引き続きインプットとアウトプットを勤めて参ります。

参考記事

10
3
0

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
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?