JavaScriptでDOMはどうやって動いているのか?その背景事情

2021年2月21日JavaScript

ブラウザが、HTMLを読み込むとDOM(Document Object Model、文書オブジェクトモデル)と呼ばれるデータ構造が作られます。
DOMはHTMLやXMLという文書のためのAPI(アプリケーションプログラミングインターフェイス)です。
このDOM APIを通して、JavaScriptは、HTMLの情報を取得したり、変更したりすることができます。

DOMを操作できるようになると、あらゆる面白いことをブラウザ上でできるようになるので、学んでいきましょう。

また、開発者として知っておきたい「JavaScriptでDOMはどうやって動いているのか?」その背景事情についてまとめました。

DOMとはなにか?

DOMというのは、JavaScriptのコードとブ ラウザの間で動くインターフェースのことです。もう少し詳しくいうと、ブラウザにレンダーされるHTMLドキュメントのことです。
JavaScriptはブラウザが常に必要というわけではありませんが、Webページを操作するときには、必ずDOMが関わってきます。

JavaScriptでDOMを操作するとHTMLが書き換えられる?

JavaScriptが操作しているのは、あくまでもDOMであってHTML自体ではありません。
JavaScriptでHTMLを書き換えてもあくまでDOM上を書き換えているだけであって、元のHTMLはそのままということを理解しておきましょう。

DOMの用語について

まずはDOMの用語について整理しておきましょう。

<!DOCTYPE html>
<html>
  <head>
    <title>DOMについて</title>
  </head>
  <body>
    DOMというのは...
  </body>
</html>

まず、左のHTMLのDOMは右になります。
一つひとつのデータをNodeといいます。
そのNodeの塊が枝分かれしているので、そのデータ構造全体をNodeツリー(もしくはDOMツリー)といいます。

DOMでは改行と空白も一つのNodeとしてカウントされます。

documentとdoctype以外のhtmlの要素は、要素Node(もしくはElement Node)といいます。

テキストや空白・改行は、Text Nodeと呼ばれます。

親子関係を表すNodeでParent NodeとChild Nodeがあります。
Parent Nodeはたとえば、headからみたhtmlです。
Child Nodeは、headからみたtitleのことです。

また兄弟(Sibling)関係のNodeをあらわすSibling Node(シブリングノード)があります。
Sibling Nodeはたとえばhtmlの子Node同士は兄弟関係にあるので、headとbodyはsibling Nodeです。

DOMがあるからできること

DOMがあることで、JavaScriptとブラウザが相互作用できます。
相互作用というのは、JavaScriptでHTML要素を作ったり、変更したり、削除したりできるということです。
それだけでなく、スタイルを設定したり、クラスや属性もいじることができますし、クリックされたら何々するといったイベントを設定して反応することができます。
DOMツリーというのが、HTMLドキュメントから生成されます。
DOMはとても多くのメソッドとプロパティを持っているAPIで、それがゆえにDOMツリーと相互作用できます。

Nodeについて

HTML要素はNodeという単位に分けられます。それはJavaScriptのオブジェクトがそれぞれのNodeの特別なメソッドやプロパティにアクセスします。(textContent,childNodes,parentNode,cloneNode())といったものがあります。

DOM APIは、Nodeに分けられます。そのNodeにはタイプがあります。

  • Element type
  • Text type pタグなど
  • Comment type コメントなど
  • Document type

といったタイプがあります。

そしてそれぞれが次のようなメソッドやプロパティがあります。

  • innerHTML
  • classList
  • children
  • parentElement
  • append()
  • remove()
  • insertAdjacentHTML()
  • querySelector()
  • closest()
  • matches()
  • scrollIntoView()
  • setAttribute()

それぞれの要素がオブジェクトとして表現されます。

要素ごとに異なるユニークなプロパティ

Elementには、HTMLElementがあり、その中にはHTMLButtonElement, HTMLDivElement,などたくさんあります。
HTML要素ごとに、HTML要素の異なるタイプがあります。
それぞれのHTML要素が異なるユニークなプロパティを持っています。
image要素なら、src属性のプロパティをもっていますが、それは他のHTML要素にはありません。
アンカーリンクもhref属性がありますが、他のHTML要素にはありません。
そのため、DOM APIにはそれぞれの異なるHTML要素が作られます。

継承について

大事な機能として、継承があります。
メソッドとプロパティを継承できます。
継承とは、子Nodeは、親Nodeのメソッドやプロパティにアクセスできるということです。
HTMLは、他のすべてのエレメントタイプ、Nodeタイプからアクセスされます。すべてのメソッドやプロパティが使えるということです。
HTMLのボタンelementであれば、要素であり、Nodeです。
つまり、Nodeタイプそれぞれにユニークなメソッドやプロパティがあり、さらにいくつかのタイプは祖先からの継承によってさらなるメソッドやプロパティを保持します。
ドキュメントNodeタイプも一つのNodeタイプです。querySelector、createElementといったとても良く使うメソッドやプロパティを保持した、一つのNodeタイプです。querySelectorはドキュメントでもelementタイプでもどちらでも利用できます。

addEventListenerがどれでも使える理由

addEventListenerはなぜどのNodeタイプでも使えるのでしょうか?
それは、EventTargetという特別なNodeが存在しているためです。
EventTargetは、NodeタイプとWindowのNodeタイプの親にあたります。
ここでも継承があるために、DOM APIでaddEventListenerはどのNodeタイプでも使うことができます。

ちなみに、EventTargetオブジェクトは手動で作ることはできません。