ことれいのもり

【JavaScript】CSSの干渉を防ぐシャドウDOMの使い方

はじめに

この記事では、JavaScriptでダブルクリックをしたときにシャドウDOMを追加する方法を紹介します。

シャドウDOMを知らない方でも分かるように説明しているので、ぜひ最後までご覧ください!

シャドウDOMとは?

シャドウDOMとは、通常のDOMから分離されたDOMのことです。

より分かりやすく言い換えると、通常のDOMから干渉を受けない隠された空間を作り出すことができます。

メリットは次の2つです。


  1. 外部のCSSの影響を受けない
  2. 外部のJavaScriptの影響を受けない


例えばあなたがChrome拡張を自作したいとしましょう。

既に存在しているWebページにdiv要素を追加して、CSSのidを追加します。

このとき、CSSのidはページ内で被ってはいけません!

自分で作ったページなら、id被りを防ぐことは簡単です。

しかし、どんなWebページとも被らないid名をつけることは難しいです。


ここで登場するのがシャドウDOMです。

通常のDOMとは別の空間を作り出すため、id名はシャドウDOM内で被らなければ良いのです!!

このように、外部から影響を受けたくないときに効果を発揮するのがシャドウDOMです。

ダブルクリックをしたらシャドウDOMを追加するプログラム

それでは実際のコードで使ってみましょう。

普通に追加しても面白くないので、今回はダブルクリックをしたらそのテキストを取得してシャドウDOM内に表示してみます。


全体のコードは次の通りです。


document.addEventListener('dblclick', function () {
  // クリックしたテキストを取得
  const clickText = window.getSelection().toString();

  if (clickText) {
    // Shadow DOMのルートを作成
    const host = document.createElement('div');

    // ルートの中の要素を追加
    const shadow = host.attachShadow({mode: "open"});
    const span = document.createElement("span");

    // spanの中に表示する文字列
    span.textContent = "ここに" + clickText + "が入ります";

    // DOMを追加
    shadow.appendChild(span);
    document.body.appendChild(host);
  }
});


いくつかポイントを絞って紹介します。


ダブルクリックの検知

document.addEventListener('dblclick', function () {
    ...
}


ダブルクリックの検知は、'dblclick'のイベントを指定するだけです。

この中に書かれたプログラムは、ダブルクリックがされた後にのみ実行されます。

クリックしたテキストの取得

const clickText = window.getSelection().toString();


ユーザーがクリックしたテキストを取得したいときは、getSelection()を使います。 返ってくるのはSelectionオブジェクトという、選択した文字列に関する情報が入っているオブジェクトです。 今回は文字列だけがほしいので、toString()で文字列に変換して格納します。

シャドウDOMの生成

// Shadow DOMのルートを作成
const host = document.createElement('div');


ここからがシャドウDOMを作っている部分です。

まずは通常のDOMを使ってルートと呼ばれる大枠を作ります。

これは、通常のDOMとシャドウDOMをつなぐ接点のようなものです。

今回はルートに<div>を使い、この中にシャドウDOMを追加していきます。


// ルートの中の要素を追加
const shadow = host.attachShadow({mode: "open"});
const span = document.createElement("span");

// spanの中に表示する文字列
span.textContent = "ここに" + clickText + "が入ります";


シャドウDOMの生成はattachShadow()を使います。

mode: "open"は外部のJavaScriptからこのシャドウDOMにアクセスできることを意味しています。

逆にmode: "close"にすることで外部から中身を触れないようにもできます。

オプションは色々な種類があるので、公式ドキュメントを見るのがオススメです!


シャドウDOMを作った後は、普通のDOMと同じように要素を追加します。

今回は<span>を生成し、その中にクリックした文字列を表示できるようにしました。

最後に、appendChild()でDOMを追加すれば完成です!

実行結果

今回はChrome拡張作成で動かしてみました。

ダブルクリックすると、bodyの一番最後にシャドウDOMを追加しています。


ダブルクリックイベントを発火する


まずは、適当なWebページ上でダブルクリックをします。

青色に変化している部分がダブルクリックで選択された箇所です。(getSelection()で取得する部分)


シャドウDOMが追加


すると、Webページの一番下に選択された箇所を含んだDOMが追加されました!

見た目は普通のDOMですが、シャドウDOMです。


シャドウDOMが追加されたコード


デベロッパーツールでコードを確認します。

shadow-rootと書かれた要素が追加されていますが、これがシャドウDOMです!


見た目は普通のDOMですが、実際はシャドウDOMなので本来のWebページの影響を受けません。

このように、既存ページの影響を受けたくないときに役立つ機能です!

参考にしたサイト

シャドウDOM:MDN - シャドウ DOM の使用

ダブルクリックイベント:MDN - Element: dblclick イベント

選択したテキストの取得:MDN - Window.getSelection()