💭

なぜHTMLコメントにはイベントリスナーが付与できるのか

2023/08/15に公開

HTMLのコメントにはaddEventListenerremoveEventListenerなどのイベント関連の処理が実装されています。
DOMの構造に詳しい方であれば、このような仕様になっている理由が分かる方も多いかと思われます。
今回はHTMLのコメントの仕様を通じてDOMについての理解を深めるための記事を書きます。

前提条件

この記事は下記の内容を前提として記載しています。

  • JavaScriptの基礎的な構文を理解している
  • 基本的なHTML要素を把握している
  • オブジェクト指向プログラミングについての基礎的な理解がある

最後の「オブジェクト指向プログラミングについての基礎的な理解がある」については推奨ですが必須ではありません。

確認手順

まずは本当にコメントにイベントリスナーが実装されているかを確認します。
確認のためnew Comment()でHTMLのコメントを作成し、typeof演算子でaddEventListenerが関数として存在するのかを確認[1]しています。

const comment = new Comment('foo');
// => <!--foo-->
console.log(typeof comment.addEventListener);
// => "function"

確認の結果typeof演算子によって"function"が返却されaddEventListenerという関数が登録されていることが確認できました。

DOMの構造についての解説

実際にメソッドが存在することは確認できたので、次になぜこのような仕組みになっているのかを解説していきます。

なぜコメントにイベントリスナーが存在するのか

答えを先に出すと、コメントにもaddEventListener/removeEventListenerが存在する理由としては、CommentインターフェイスがEventTargetインターフェイスを継承しているからです。

https://developer.mozilla.org/ja/docs/Web/API/EventTarget

上記の理由がどういう意味なのかを実際にブラウザのコンソールなどを使って確認してみます。

実際にインターフェイスの継承構造をたどってみる

まずは継承構造の確認から始めようと思います、ChromeなどのコンソールではDOMのプロパティが確認できるためそれを利用して確認を行います。
先程と同じようにnew Comment()でコメントを作成してみてその中身をブラウザのコンソールで確認してみましょう。

const comment = new Comment('foo');
console.log([comment]);

次にコンソール上でコメントオブジェクトを配列から展開して、[[Prototype]]という項目を何度も開いていくと下記のような順番で開いていくと思います。

  1. Comment
  2. CharacterData
  3. Node
  4. EventTarget
  5. Object

この作業はDOMの継承関係を遡っていく作業だと思ってください。
この内ObjectはJavaScript全体の環境まで飛び出しているのでDOMの世界としてはEventTargetが最上位のインターフェースとなります。

HTMLButtonElement(button要素)の場合を例とすると下記の図のような継承が行われています。

EventTargetインターフェイスはaddEventListenerなどが存在する、NodeインターフェイスはappendChild・textContentなどが存在する、ElementインターフェイスはqueryセレクターメソッドやinnerHTMLが存在する、HTMLElementインターフェイスはclickメソッドやinnerTextなどが存在する、HTMLButtonElementインターフェイスはdisabledやtypeなどが存在する。
HTMLButtonElementのDOMインターフェイス継承関係を表した図

解説のためにbutton要素を出してしまいましたが、コメントと比較した場合どちらもNodeインターフェイスまでは共通しています。
そのためbutton要素もコメントもNodeインターフェイスが持っているメソッドやプロパティを利用することができることが分かります。
もしbutton要素についても継承の確認を行いたい場合は先程のコードからnew Comment('foo')の部分をdocument.createElement('button')に変更することで確認可能です。

このようにDOM上の様々な要素は対応するインターフェイスを継承することである程度の分類が行われています。
そのため、EventTargetを継承しているコメントもbutton要素も同じようにイベントリスナーを設定することができます。

EventTargetインターフェイスとはなにか

今までの説明や名前からも想像できるようにEventTargetはイベントを受け取るためのメソッドを定義したインターフェースです。
そのため、イベントを受け取ることができる要素やオブジェクトはEventTargetインターフェースを継承しています。
図にも記載があったようにaddEventListenerremoveEventListenerなどはこのインターフェイスで定義されています。
ブラウザで利用できるAPIにはHTMLの要素以外にも(Windowなど)イベントを受け取ることができるオブジェクトが存在するためこのインターフェースが最上位に位置しています。

まとめ

以上の確認からHTMLのコメントにもaddEventListenerが存在する理由として、コメントがEventTargetインターフェイスを継承している構造が理解できたかと思われます。

このようにDOMの構造を理解することで、要素が持つプロパティやメソッドが要素固有のものなのか、それともすべての要素が持っているものなのかなどが理解しやすくなります。
またTypeScriptを利用している場合はinstanceof演算子を利用した型ガードなどがより正確に記述できるようになります。
普段何気なく利用しているHTMLやDOMですが、少し深掘りしてみるとまた見え方が変わってきてDOMの扱いもより正確に記述できるようになります。
MDNのDOMの紹介ページなどではより詳しい解説などもありますので、もし興味が湧いたらぜひ読んでみてください。

参考資料

https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction
https://developer.mozilla.org/ja/docs/Web/API/EventTarget

脚注
  1. Google Chrome 115 及び Firefox 116 にて確認 ↩︎

GitHubで編集を提案

Discussion