shadow DOM は「外側から見えない、部品専用のプライベート DOM」
まず一言でいうと、shadow DOM は
「コンポーネントの中だけで完結する、外側から直接いじれない専用 DOM」 です。
普通の DOM は、ページ全体で 1 本の大きな木になっています。
CSS も JavaScript も、その木のどこにでも届いてしまうので、
ボタン用の CSS が他のボタンにまで当たってしまう
ライブラリのスタイルと自分のスタイルがぶつかる
コンポーネント同士が互いの DOM を壊してしまう
みたいな「干渉事故」が起きやすい。
shadow DOM は、
「このコンポーネントの中は、このコンポーネント専用の世界にする」
という仕組みです。
外側の CSS が中に勝手に入ってこない
中の CSS も外側に漏れない
中の DOM 構造も、外からは“見えない”
こうやって、コンポーネントを「カプセル化」するのが shadow DOM の役割です。
まずは「shadow root を作る」イメージを掴む
要素に「影の DOM」をくっつける
shadow DOM の入口は attachShadow です。
<div id="my-component"></div>
const host = document.querySelector("#my-component");
// ここで shadow DOM をくっつける
const shadow = host.attachShadow({ mode: "open" });
// shadow の中にだけ有効な DOM を作る
shadow.innerHTML = `
<style>
p {
color: red;
font-weight: bold;
}
</style>
<p>これは shadow DOM の中のテキストです</p>
`;
JavaScriptここで起きていることを整理すると、
host(#my-component)が「ホスト要素」になるattachShadow で「shadow root(影のルート)」をくっつけるshadow の中に書いた HTML は、ページの DOM とは別世界になる
という流れです。
ブラウザの開発者ツールで見ると、#my-component の中に #shadow-root という特別な領域が見えるはずです。
なぜわざわざ「別世界」にするのか
一番大きな理由は 「スタイルと構造のカプセル化」 です。
shadow DOM の中で書いた CSS は、
基本的にその shadow DOM の中にしか効きません。
外側の p { color: blue; } などのスタイルも、
中の <p> には届きません。
つまり、
このコンポーネントの見た目は、この中だけで完結させる
外の CSS に壊されない
逆に、このコンポーネントの CSS も外を汚さない
という状態を作れます。
「コンポーネントを部品として配布したい」
「他の人の CSS と絶対にぶつかりたくない」
というときに、shadow DOM は本気を出します。
重要ポイント:shadow DOM の「3つの登場人物」
ホスト要素(host)
shadow DOM をくっつける側の要素です。
const host = document.querySelector("#my-component");
JavaScriptこの要素は、外側の DOM から見える存在です。
ただし、中身は shadow DOM によって「隠される」ことがあります。
shadow root(影のルート)
attachShadow で作られる、shadow DOM の入り口です。
const shadow = host.attachShadow({ mode: "open" });
JavaScriptこの shadow の中に、
HTML や CSS を書いていきます。
ここが「コンポーネント専用の小さな DOM ツリー」になります。
shadow DOM の中身(子要素たち)
shadow.innerHTML = ... で書いた要素たちが、
shadow DOM の中身です。
外側の DOM から querySelector しても、
基本的には直接触れません。
ただし、mode: "open" の場合は、host.shadowRoot 経由で中を触ることができます。
例題:シンプルな「バッジコンポーネント」を shadow DOM で作る
HTML
<div id="badge-host"></div>
JavaScript
const host = document.querySelector("#badge-host");
const shadow = host.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
.badge {
display: inline-block;
padding: 4px 8px;
border-radius: 999px;
background: #ff4757;
color: white;
font-size: 12px;
}
</style>
<span class="badge">NEW</span>
`;
JavaScriptここでのポイントは、
.badge のスタイルは shadow DOM の中だけに効く
外側で .badge { background: green; } と書いても、このバッジには影響しない
逆に、このバッジ用の CSS が外の .badge にも影響しない
という「完全に閉じた部品」になっていることです。
これが、shadow DOM の一番分かりやすい価値です。
外側からはどう見えるのか?「ライト DOM」との違い
普通の DOM(ライト DOM)
いつも触っている DOM は、
「ライト DOM」と呼ばれることがあります。
<div id="app">
<p>こんにちは</p>
</div>
この <p> は、document.querySelector("p") で普通に取れますし、
CSS も p { ... } でスタイルできます。
shadow DOM の中身は、外からは“見えない”
さっきのバッジの例でいうと、
外側から document.querySelector(".badge") をしても、
shadow DOM の中の .badge はヒットしません。
これは、
コンポーネントの中身を外から勝手にいじらせない
外側の CSS が中に入り込まない
という「カプセル化」のためです。
ただし、mode: "open" の場合は、host.shadowRoot.querySelector(".badge") のようにして、
「意図的に」中を触ることはできます。
重要ポイント:mode: “open” と “closed”
open モード
const shadow = host.attachShadow({ mode: "open" });
JavaScriptこの場合、host.shadowRoot で shadow root にアクセスできます。
開発中や学習中は、
基本的に open を使うと良いです。
closed モード
const shadow = host.attachShadow({ mode: "closed" });
JavaScriptこの場合、host.shadowRoot は null になります。
つまり、外側のコードからは
shadow DOM に直接アクセスできません。
ライブラリ作者が
「中身は絶対に触ってほしくない」
というときに使うことがあります。
初心者のうちは、
「open は触れる、closed は触れない」
くらいの理解で十分です。
shadow DOM と Web Components の関係
カスタム要素と組み合わせると「本物のコンポーネント」になる
shadow DOM は単体でも使えますが、
本領を発揮するのは カスタム要素(Custom Elements) と組み合わせたときです。
例えば、こんな感じ。
class MyBadge extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
.badge {
display: inline-block;
padding: 4px 8px;
border-radius: 999px;
background: #1e90ff;
color: white;
font-size: 12px;
}
</style>
<span class="badge"><slot></slot></span>
`;
}
}
customElements.define("my-badge", MyBadge);
JavaScriptHTML ではこう書けます。
<my-badge>NEW</my-badge>
<my-badge>SALE</my-badge>
ここで起きていることは、
<my-badge> というタグを自分で定義
その中身は shadow DOM で完全にカプセル化
外側からは「ただのタグ」として使える
という流れです。
これが「Web Components」の世界で、
shadow DOM はその中核にある技術です。
初心者向けにまとめる「shadow DOM をどう捉えるか」
ざっくりイメージ
コンポーネントごとに「自分専用の DOM と CSS の世界」を持てる
外側のスタイルや DOM 操作に邪魔されない
逆に、自分のスタイルも外を汚さない
カスタム要素と組み合わせると、本当に「部品」として配布できる
というのが、shadow DOM の本質です。
いつ使うと嬉しいか
再利用可能な UI 部品を作りたいとき
他人の CSS と絶対にぶつかりたくないとき
ライブラリとして配布するコンポーネントを作るとき
普通の小さな学習用コードでは、
いきなり必須ではありません。
でも、「コンポーネントをちゃんと作る」段階に来たとき、
shadow DOM の考え方を知っているかどうかで、
設計の質が大きく変わります。
最後に:今の段階で本当に掴んでほしいこと
shadow DOM は「コンポーネント専用のプライベート DOM」attachShadow({ mode: "open" }) でホスト要素に影の DOM をくっつける
中で書いた HTML/CSS は、基本的に外と干渉しない(カプセル化)
外から querySelector しても中身は見えない(ただし open なら shadowRoot 経由で触れる)
Web Components(カスタム要素)と組み合わせると、本物の再利用可能コンポーネントになる
まずは、div に対して attachShadow して、
中に <style> と <p> を書いてみてください。
外側で p { color: blue; } と書いても、
shadow DOM の中の <p> が赤いままなのを確認できたら、
「お、確かに別世界になってるな」という感覚が、ちゃんと自分のものになります。

