Skip to content
This page has been auto-translated and may contain errors.View in English

DOM

docs.scrimba.com

HTMLファイルは一度読み込まれ、その後ページはそこにあります。しかし、入力時に結果を提案する検索ボックス、クリックで開くメニュー、カウントアップするカウンター、これらのどれもHTMLファイルには含まれていません。ページ読み込み後に何かがページ上に表示される内容を変更し、その何かはJavaScriptがDOMを通じてページに到達することです。この章はその到達について、つまりJavaScriptがページの部分を見つけてそれらを変更し、ページがリーダーの前で更新されるしくみについてです。

DOMとは何か

ブラウザがページを読み込むとき、HTMLを読んで、それから**オブジェクトツリー**を構築します:ページ用に1つのオブジェクト、<body>用に1つ、その内部の各見出し、段落、ボタンに1つずつ。そのツリーはDOM、Document Object Modelの略です。JavaScriptはこのツリーを読むことも変更することもでき、そうするとページは直ちに更新されます。

押さえておく価値がある部分:DOMはあなたのHTMLファイルではありません。ページのライブバージョン、ブラウザのメモリ内に存在するものです。.htmlファイルは最初のレシピ、DOMはブラウザが提供している完成した料理で、JavaScriptは皆がまだ食べている間にプレートを並べ替えることができます。

DOM(Document Object Model)は、ブラウザがHTMLから構築する**メモリ内ツリー**です。すべての要素がオブジェクトになり、タグがネストされているのと同じようにネストされます:<body><main>を保有し、<main><section>を保有し、<section>は段落を保有します。JavaScriptは、グローバルなdocumentを通じてこのツリーへのハンドルを取得し、そこからツリーを歩き、任意の要素を読み、変更することができます。

後で混乱を避ける重要な区別は、DOMがソースHTMLから分離されているということです。HTMLは一度解析されてツリーを構築します。その後、ツリーがライブなものです。DOMを変更するとページは更新されます。ディスク上の元のHTMLファイルは変わりません。そのため、JavaScriptが実行されたら「ソースを表示」が画面に見えるものとまったく似ていないことができるのです。

DOMは、ブラウザのドキュメントのライブなオブジェクトモデル:ブラウザがHTMLを解析して構築する**ノード**(要素、テキスト、コメント、ドキュメント自体)のツリー、それをレンダリングするもの。それは言語に中立なAPIで、documentとそれにぶら下がっているオブジェクトを通じてJavaScriptに公開されていますが、JavaScript言語の一部ではなく、ランタイムが提供するブラウザ機能です。

実践上重要なフレーミング:HTMLソースは入力で、一度ツリーに解析され、その後DOMが真実の源です。スクリプト実行後にdocument.body.innerHTMLを読むと、配信したバイトではなく、現在のシリアライズされたツリーを取得します。これはDOMがHTMLから直接来ているためでもあります。作成するタグは初期ツリーを定義し、JavaScriptがすることすべてはそのツリーの編集です。DOMを操作することはソースが持たないコストを持ちます。変更はブラウザがレイアウトを再計算し、再描画するきっかけになることができるためで、これは下記の高度なセクションで追う糸です。

DOMのすべての要素はオブジェクトで、オブジェクトで出会ったのと同じ種類で、読むと変更できるプロパティを持っています。それが全トリックです:ページはオブジェクトで、そしてあなたはすでにオブジェクトの扱い方を知っているので、ページを変更することはオブジェクトプロパティを変更することです。

JunoDOMとは何か ブラウザはHTMLを、DOMと呼ばれるオブジェクトツリーに変え、そのツリーがライブなページです。HTMLファイルはレシピです。DOMは提供されている食事で、JavaScriptはテーブルにある間それを変更することができます。すべての要素はオブジェクトなので、ページを変更することはオブジェクトプロパティを変更することを意味します。
JunoDOMとは何か DOMは、ブラウザがdocumentを通じてアクセスするHTMLから構築されるメモリ内ツリーです。それはソースファイルから分離されています:一度解析してから、ツリーがライブで、「ソースを表示」が見えるものと異なることができる理由がこれです。ツリーを変更してページが更新されます。
JunoDOMとは何か DOMはブラウザがHTMLから解析してレンダリングするノードのツリーです。それはブラウザAPI、言語の一部ではありません。ソースは一度解析された入力で、その後ツリーが真実なので、innerHTMLは配信したバイトではなく、現在のツリーを反映します。編集はレイアウトと再描画のコストを持つことができ、それが書き込みパターンが後で重要な理由です。

要素の選択

要素を変更する前に、ツリー内でそれを見つける必要があります。CSSと同じ方法で、セレクターを使って指し、ブラウザは一致するオブジェクトを返します。

主力はdocument.querySelectorです。あなたは**CSSセレクター**を渡します。CSSで使うのと同じ種類のパターンで、一致する最初の要素を返します:

js
const card = document.querySelector(".card"); // class="card" を持つ最初の要素
const total = document.querySelector("#total"); // id="total" を持つ要素

.cardはクラスで照合、#totalはidで照合、"button"のような単語はタグ名で照合します。querySelectorは最初の照合を取得してそこで停止します。すべての照合が欲しいとき、ループできるリストを返すquerySelectorAllを使います:

js
const cards = document.querySelectorAll(".card"); // class="card" を持つすべての要素
console.log(cards.length); // それがいくつ見つけたか

何も照合しない場合、querySelectornullを返し、これはJavaScriptが「ここには何もありません」と言う方法です。

document.querySelectorはあらゆるCSSセレクターを取り、最初の照合要素または何も照合しない場合はnullを返します。document.querySelectorAllはリストとしてすべての照合を返します:

js
const toggle = document.querySelector(".menu-toggle"); // 最初の照合、または null
const items = document.querySelectorAll(".card"); // すべての照合、リスト

セレクターは完全なCSS、クラスとIDだけではなく:querySelector("nav a")はnavの内部の最初のリンクを見つけ、querySelector("input[type='email']")は属性で照合します。スタイルシートで選択できるものはすべてここで選択できます。

null戻りが計画する1つです。ミスがnullであるため、document.querySelector(".missing").textContentのようなコードはスローします。nullのプロパティを読むことはできないからです。要素が存在しない可能性があるとき、最初にチェック:if (toggle) { ... }

document.querySelector(selector)はドキュメント順序でCSSセレクターに照合する最初の要素を返し、nullを返します。document.querySelectorAll(selector)はすべての照合のNodeListを返します。どちらも完全なCSSセレクター文法を受け入れるので、コンビネーター、属性セレクター、疑似クラスはすべて機能します:

js
const firstError = document.querySelector(".form .field.is-invalid input");
const externalLinks = document.querySelectorAll("a[href^='http']");

結果の2つのプロパティを知る価値があります。まず、querySelectorAllからのNodeList静的です:呼び出し時に取得されたスナップショットで、その後ページに追加された要素はそれに現れません。それは古いAPI(getElementsByClassNamegetElementsByTagName)と異なります。これらはDOMが変更されると自分自身を更新するライブHTMLCollectionを返します。最後のセクションでそれぞれの違いについて詳しく説明します。次に、スコープはパフォーマンスと正確さに重要です:documentではなく要素上でquerySelectorを呼び出すと、その要素のサブツリーだけを検索します(card.querySelector(".title"))。これは大きなページではより速く、ページ中のどこかで照合することから免疫があります。コンテナーを既に保有しているときはいつでもスコープ付きフォームに手を伸ばしてください。

Juno要素の選択 要素を見つけるには、document.querySelectorにCSSセレクター(.cardまたは#totalなど)を渡して、最初の照合を取得します。すべてをリストとして取得したいときはquerySelectorAllを使用します。何も照合しない場合はnullを取得します。これは「ここには何もありません」を意味します。
Juno要素の選択querySelectorは最初の照合を返し、querySelectorAllはリストとしてすべての照合を返し、どちらもスタイルシートで使うあらゆるCSSセレクターを受け入れます。ミスはnullを返すので、そこからプロパティを読むとスローされます。要素が存在しない可能性があるときはif (element)でガード。
Juno要素の選択 どちらのメソッドも完全なCSSセレクターを受け入れます。querySelectorAllは静的なNodeListを渡して戻ります。それは後の追加を見ることはありません。documentではなく要素上でquerySelectorを呼び出してそのサブツリーだけを検索してください。これはより速く、ページ中のどこかで照合することを避けます。そしてミスがnullであることを思い出してください。

要素の読取と変更

要素を保有すると、オブジェクトのプロパティを読取りと変更する場合と同じ方法で、そのプロパティを通じて読取りと変更します。ここがページが実際に動き始めるところです。

最も使うプロパティはtextContentで、要素内のテキストです。読むと設定できます:

js
const total = document.querySelector("#total");
total.textContent = "42 items"; // ページは「42 items」を表示

見た目を変更するには、通常クラスを追加または削除して、CSSにスタイリングを行わせます。classListにはそのための3つのメソッドがあります:

js
const card = document.querySelector(".card");
card.classList.add("selected"); // クラスを追加
card.classList.remove("selected"); // それを取り除く
card.classList.toggle("selected"); // 足りなければ追加、あれば削除

.styleを通じて単一のスタイルを直接設定でき、setAttributesrcまたはhrefのような属性を設定できます:

js
card.style.color = "crimson"; // 1つのインラインスタイル
const avatar = document.querySelector(".avatar");
avatar.setAttribute("src", "太郎.jpg"); // 画像ソースを設定

classListに最初に手を伸ばし、.styleは1回限りの調整にのみ使ってください。スタイリングをCSSに保つことは、それを1か所に保つためです。

textContentは要素内のテキストを読み書きします。classListはクラスをトグルしてスタイリングをCSSに保ちます。.styleは個別のインラインスタイルを設定し、setAttributeはあらゆる属性を設定します:

js
const badge = document.querySelector(".badge");
badge.textContent = `${count} new`; // テキストを設定
badge.classList.toggle("is-hidden", count === 0); // 条件とともにトグル
badge.setAttribute("aria-label", `${count} new messages`); // 属性を設定

classList.toggleはオプションの2番目の引数を取ります:trueのときクラスをオンにし、falseのときオフにするブール値で、addremoveの周りのif/elseよりクリーンです。

早期に述べる価値がある注意:またinnerHTMLがあり、要素のコンテンツをタグを含む生のHTMLとして設定します。柔軟なように見えるため魅力的ですが、ユーザーからのテキストにはinnerHTMLではなくtextContentを使ってください。innerHTMLをユーザー入力から設定すると、その入力はページに実のHTMLを注入させることができ、セキュリティホールです。プレーンテキストを設定しているとき、textContentは正しいツールとにかくです。

読み取りと書き込みサーフェスは広いですが、4つのプロパティがほとんどの仕事を行います:テキスト用のtextContent、状態クラス用のclassList、計算された1回限りのスタイル用の.style、属性用のsetAttribute。プレゼンテーションがCSSに存在し、JavaScriptが**状態クラス**(is-openまたはis-loadingのようなクラスであって、あなたのスタイルシートはそれをスタイルする)をフリップするだけなので、.styleよりclassListを優先:

js
const panel = document.querySelector(".panel");
panel.classList.toggle("is-open", shouldOpen);
panel.setAttribute("aria-expanded", String(shouldOpen)); // 属性は文字列

鋭いエッジはinnerHTMLです。設定すると文字列をHTMLとして解析し、そこから実のノードを構築するので、その文字列内のあらゆるマークアップはライブになります:<script>またはユーザー提供のテキスト内のonerrorハンドラーはページで実行されます。それはクロスサイトスクリプティング(XSS):テキストとして扱ったデータを通じて実行可能なコンテンツを注入。textContentはHTMLを解析しません。それはテキストだけを設定し、のみテキストだけで、それがデフォルトで安全で、ブラウザがHTMLパースをスキップするため、より速いです。innerHTMLを完全に制御するマークアップに制限し、ユーザー入力を連結することでそれを構築しない。構造化されたマークアップにユーザーデータを挿入する必要があるとき、安全なメソッドで構造を設定し、textContentを通じて信頼できない値をドロップ。これは次のセクションが構築するパターンです。

属性についての1つ以上のメモ:setAttributeはHTML属性(文字列)を書き込みますが、多くの要素は異なる可能性があるライブなプロパティも公開します。input.valueはユーザーが今タイプしたものを反映します。input.getAttribute("value")はマークアップから元のデフォルトを返します。フォームフィールドについては、属性ではなくプロパティを読んでください。

Juno要素の読取と変更textContentで要素内のテキストを設定して、classList.addremovetoggleでクラスを追加または削除することで見た目を変更します。1回限りの調整には.styleを、画像ソースのようなものsetAttributeを使ってください。クラスに寄りかかってスタイリングをCSSに保ってください。
Juno要素の読取と変更 テキスト用のtextContent、状態クラス用のclassList、1回限り用の.style、属性用のsetAttributeclassList.toggleはそれをオンまたはオフにするブール値を取ります。ユーザーがタイプした何かについてはinnerHTMLから遠ざかってください。ページに実のHTMLを注入するためです。プレーンテキストについては、textContentは正しいツール、とにかくです。
Juno要素の読取と変更classListで状態クラスをフリップしてスタイリングをCSSに保ちます。属性についにはsetAttributeを使ってください。それらは文字列であることを思い出してください。innerHTMLは文字列をライブHTMLとして解析するので、ユーザーデータはXSSホールです。textContentは解析せず、安全で速い。フォームフィールドについては、属性ではなくライブなvalueプロパティを読んでください。

要素の作成と削除

既に存在する要素を変更するとあなたは遠いところに到達しますが、時々何もない:追加しているto-do、到着する検索結果。そのためにあなたは新しい要素を構築してツリーに入れ、古いものを取り出します。

document.createElementで新しい要素を作り、何かコンテンツを与えてから、appendでページに追加します:

js
const list = document.querySelector(".todo-list");
const item = document.createElement("li"); // 新しい <li>、ページ上にはまだない
item.textContent = "Buy oat milk"; // テキストを与える
list.append(item); // これでリストの最後に表示されます

createElementからの新しい要素は、あなたが何かにappendするまでメモリの中だけに存在します。その手順がそれをページに置く。要素をページから削除するには、それにremoveを呼び出します:

js
item.remove(); // ページから消える

document.createElement(tag)は要素を構築し、appendはそれを親の最後の子として追加し、removeはツリーから要素を削除:

js
const list = document.querySelector(".todo-list");
const item = document.createElement("li");
item.textContent = "Buy oat milk"; // 安全:テキスト、HTMLとして解析されない
item.classList.add("todo-item");
list.append(item); // ページに接続

HTMLを構築する前にテキストをtextContentで設定し、文字列ではなく、ユーザー入力されたテキストはマークアップを注入できません。appendは単純な文字列と複数のノードも一度に受け入れます(list.append(item, "or", another))。これは混合コンテンツに便利です。

人々を捕捉する詳細:要素の作成はそれ自体に何も見えるものを行いません。それはツリーに既に存在するものに接続するまで、メモリに座ります。その時まで、それは実ですが画面外で、これは高度なセクションが効率的に構築するために使うプロパティ正確に。

createElementは切り離されたノードを構築し、appendはノード(または文字列)を親の最後の子として挿入し、removeはツリーからノードを削除。効率の質問は面白い1つで、ライブツリーへの挿入はコストを持つことができるため。

DOMを幾何学に影響する方法で変更するたび、ブラウザは位置とサイズを再計算する必要があります(リフロー、レイアウトとも呼ばれる)そして再描画。その中でループで行う場合、反復ごとにライブページに1つのノードを追加して、その作業を繰り返しトリガーできます。修正はオフDOMを構築して一度挿入。DocumentFragmentは正確にはこれのための軽い、画面外のコンテナ:あなたはそれにノードを追加(それは、ライブツリーの中にはないので、リフローはない)してから、フラグメントを追加して、1つの操作としてその子を移動:

js
const list = document.querySelector(".results");
const fragment = document.createDocumentFragment(); // 画面外コンテナ

for (const name of names) {
  const li = document.createElement("li");
  li.textContent = name; // ユーザーデータの安全な挿入
  fragment.append(li); // リフローなし:フラグメントはオフDOM
}

list.append(fragment); // ライブツリーへの1つの挿入

フラグメントを追加するとそれを親に空にするので、フラグメント自体はDOMに現れません。ただその子。同じ原則はより広い習慣を駆動:バッチDOMは書き込み。レイアウトプロパティ(offsetHeightのような)を読むと、ブラウザはレイアウトを待つを中止させるので、ループの読み取りと書き込みをインターリーブするとレイアウト・スラッシュ、繰り返される強制リフロー。あなたの読み取りをグループ化してから、あなたの書き込みをグループ。ノードのひとつかみについては、これのどれもが重要ではありません。何百ものためそれはスムーズとぎこちない間の違い。

Juno要素の作成と削除document.createElementで新しい要素を構築して、textContentでテキストを与えて、appendでページに置いてください。新しい要素はあなたがそれを追加するまでメモリの中だけに存在するので、その手順がそれを表示させる。ページから削除するには、それにremoveを呼び出してください。
Juno要素の作成と削除createElementが構築し、appendが最後の子として接続し、removeが削除。HTMLを構築するのではなく、追加前にtextContentでテキストを設定してください。ユーザーがタイプした何かがマークアップを注入できないように。作られた要素はあなたがページ上に既に存在するものに接続するまで、見えたままであることを思い出してください。
Juno要素の作成と削除 ループでライブツリーに追加すると各時間リフロー、オフDOMでDocumentFragmentで構築してそれを一度追加してトリガーできて、子は一緒に移動。ループの途中レイアウトプロパティを読むとレイアウトを強制してスラッシュを引き起こすので、読み取りをバッチしてから書き込みをバッチ。いくつかのノードについてそれはmootです。何百ものためそれはスムーズとぎこちない。

ここからDOMはどこに行く

選択、読取、変更、作成、削除は、JavaScriptがページにすることのほとんどをカバーし、すべては下の同じ動きです:ツリー内のオブジェクトを見つけてそのプロパティを変更。DOMはオブジェクトで、オブジェクトで学んだすべてが直接ここに適用されて、この章の小さなメソッドセット(querySelectortextContentclassListcreateElementappend)はツールキットのほとんど。

これまでのところ、タイミングが足りません。すべてのサンプルはここで一度実行され、トップから下ですが、実ページはリーダーがすることに応答して変更されます:クリック、キーストロークス、提出されたフォーム。DOMの変更をそれらの瞬間に配線することはイベントで、次の章で、addEventListenerは「ユーザーがこれを行った」を「ページがそうする」に接続。選択と変更がイベントへの応答と一緒に座ると、あなたはこのハンドブックがこしらえる対話型ページを構築できます。