Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】What is Shadow DOM and How It’s Used in Chrome Extensions

2025年09月12日に「Dev.to」が公開したITニュース「What is Shadow DOM and How It’s Used in Chrome Extensions」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Shadow DOMは、WebページのDOM構造とスタイルを他から隔離し、コンポーネントを独立させる技術だ。これにより、外部CSSやJavaScriptの影響を受けず、UIの干渉を防ぎ安定性を高める。特にChrome拡張機能では、ページのスタイルやスクリプトとの衝突を回避し、拡張機能のUIを確実に動作させるために有効だ。

ITニュース解説

ウェブサイトを開発していると、「Shadow DOM」という言葉に出会うことがあるかもしれない。これは「ウェブコンポーネント」という技術群の核となるもので、ウェブページ内で特定の部品の見た目や構造を、他の部分から独立させて管理するための仕組みだ。通常のウェブページでは、CSSやJavaScriptといったプログラムがページ全体に影響を与えるため、部品同士が互いに干渉し合って予期せぬ問題が起きることがある。Shadow DOMは、このような問題を解決し、部品を安全に独立させて使うための画期的な技術なのである。

Shadow DOMは、通常のDOM(Document Object Model)要素に、「シャドウトゥリー(Shadow Tree)」と呼ばれる特別なDOM構造を付与する機能である。このシャドウトゥリーは、それ自身の独立したDOM構造とスタイル(CSS)の適用範囲を持つ。ここでいくつかの用語を整理しておこう。「シャドウホスト(Shadow host)」とは、Shadow DOMを所有する通常のDOM要素のことを指す。例えば、ウェブページ上に表示されるボタンや入力欄など、普段目にする要素がこれにあたる。「シャドウルート(Shadow root)」は、そのシャドウトゥリーの最も根元にあたる部分で、通常のDOMにおける<html>タグのような役割を果たす。「シャドウトゥリー(Shadow tree)」は、シャドウルートの中に含まれるDOM構造全体のことで、部品の実際のHTML構造がここに含まれる。そして、「シャドウバウンダリー(Shadow boundary)」は、通常のDOM(これを「ライトDOM」と呼ぶこともある)とシャドウトゥリーとの境界線を意味する。

通常のDOMでは、記述されたCSSやJavaScriptはウェブページ全体に適用されるのが基本だ。このため、複数の部品を配置した場合、ある部品のために書いたCSSが意図せず別の部品に影響を与えたり、JavaScriptの処理が干渉し合ったりすることが頻繁に起こる。しかし、Shadow DOMを導入することで、このような干渉の問題から解放され、以下の大きな利点が得られる。まず、部品の内部構造を外部から隠蔽できる。これにより、部品の利用者にとっては、その部品がどのように作られているかを気にすることなく、提供された機能だけを使えばよくなる。次に、外部のCSSが部品内部のスタイルを上書きするのを防ぐことができる。部品の見た目が、ページ全体のCSSの影響で崩れてしまう心配がなくなるのだ。最後に、部品内部のスタイルや構造が、意図せず外部に漏れ出してしまい、他の部分に悪影響を与えるのを防げる。部品が「孤立した空間」を持つことで、ウェブページ全体の安定性が向上する。

Shadow DOMの中で定義されたCSSは、そのShadow DOMの内部だけで有効となり、外部のスタイルシートからは影響を受けない。これは、部品が常に開発者が意図した通りの見た目を保つことを可能にする。イベント(例えば、ボタンがクリックされたり、文字が入力されたりする動作)の伝播についても、Shadow DOMは特別なルールを持つ。デフォルトでは、イベントはシャドウバウンダリーを越えて外部に伝播しない。つまり、Shadow DOM内の要素で発生したクリックイベントは、Shadow DOMの外にある要素には届かないのである。もしイベントを外部に伝播させたい場合は、イベントを作成する際にcomposed: trueという設定をする必要がある。これにより、イベントはシャドウバウンダリーを越えて通常のDOMツリーへと伝わっていく。

Shadow DOMの恩恵が特に顕著に現れるのが、Google Chromeの拡張機能の開発である。Chrome拡張機能では、しばしば「コンテンツスクリプト」というプログラムを使って、現在開いているウェブページに直接DOM要素やユーザーインターフェース(UI)を挿入することがある。例えば、ページの右下隅に独自のボタンを表示させたい場合などがこれにあたる。しかし、この方法にはいくつかの共通した問題が伴う。まず、「ページのCSSが拡張機能のUIを壊してしまう」という問題だ。ウェブページにはそれぞれ独自のCSSが適用されているため、拡張機能が挿入したUIの見た目が、ページのCSSによって意図せず変更されてしまうことがある。次に、「拡張機能内部で定義したクラス名やIDと、ページ既存のものが衝突してしまう」という問題。ウェブページと拡張機能が同じクラス名やIDを使ってしまうと、どちらか一方、あるいは両方の要素が正しく動作しなくなる可能性がある。そして、「ページのスクリプトやイベントと拡張機能のUIが干渉する」という問題も発生しうる。

Shadow DOMは、これらの問題に対する非常に強力な解決策を提供する。Shadow DOMを使用することで、「CSS汚染(Pollution)」を完全に防ぐことができる。ページのスタイルが拡張機能のUIに影響を与えることもなければ、その逆もない。これにより、拡張機能のUIは常に意図したデザインと動作を保てる。また、「DOMの衝突」を回避できる。たとえウェブページと拡張機能で同じクラス名やIDが使われていたとしても、Shadow DOM内は完全に隔離されているため、それらが衝突することはない。さらに、「UIの安定性」が向上する。React、Vue、Chakra UIのような人気のあるUIライブラリやフレームワークをShadow DOM内で実行しても、ページの特定のCSSによってそれらが妨害される心配がなくなる。これにより、拡張機能のUI開発は大幅に安定し、予測可能になる。

それでは、Chrome拡張機能でShadow DOMを使う基本的な手順を見ていこう。まず、Shadow DOMの親となる通常のDOM要素(シャドウホスト)を作成し、それをウェブページのbody要素などに追加する。例えば、const host = document.createElement('div'); host.id = 'my-extension-shadow-host'; document.body.appendChild(host);のように記述する。次に、このシャドウホストに対してattachShadow()メソッドを呼び出し、Shadow Rootを作成する。const shadowRoot = host.attachShadow({ mode: 'open' });のように記述する。modeには'open''closed'の2つのオプションがある。'open'モードにすると、外部のJavaScriptからShadow DOMの中身にアクセスできるようになり、開発中のデバッグが容易になる。一方、'closed'モードは、より高い隔離性を提供するが、外部からのアクセスが制限される。通常、拡張機能ではデバッグのしやすさから'open'が選ばれることが多い。

次に、作成したShadow Rootの中に、拡張機能のUIに適用したいCSSを定義した<style>要素を追加する。const styleEl = document.createElement('style'); styleEl.textContent = \ :host { all: initial; font-family: Arial, sans-serif; display: block; position: fixed; bottom: 20px; right: 20px; z-index: 999999; } button { padding: 8px 12px; font-size: 14px; } `; shadowRoot.appendChild(styleEl);のように記述する。ここで重要なのが、:hostセレクタ内でall: initial;`を設定している点である。これは、シャドウホスト要素が持っていたすべてのCSSプロパティを初期状態に戻す指示であり、ウェブページのCSSリセットによって拡張機能のUIが意図せず影響を受けるのを防ぐ効果がある。もしUIライブラリを使用する場合は、そのライブラリのスタイルを必要に応じてこのShadow Root内にインポートすることになる。

最後に、作成したShadow Rootの中に、拡張機能の実際のUI要素(例えばボタンなど)を追加する。const button = document.createElement('button'); button.textContent = 'My Button'; shadowRoot.appendChild(button);のように記述する。このようにして追加されたボタンは、Shadow DOMの内部に存在するため、そのスタイルや動作は外部のウェブページから完全に隔離される。

Shadow DOMは、DOM構造とスタイルを「カプセル化」、つまり隔離する強力な仕組みを提供する。特にChrome拡張機能において、Shadow DOMは拡張機能のUIがウェブページからの干渉を受けないように保護する上で非常に効果的である。実装する際には、attachShadowメソッドによるShadow Rootの作成、all: initialによるスタイルリセット、そしてイベント伝播のハンドリングに注意を払うことが重要となる。Shadow DOMを理解し活用することで、Chrome拡張機能のUIははるかに安定し、メンテナンスしやすくなるだろう。拡張機能を開発しているのであれば、この技術を積極的に採用する価値は間違いなくある。

関連コンテンツ