MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - DOM操作のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - DOM操作
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == DOM (Document Object Model) は、HTML または XMLドキュメントをプログラムから操作するためのAPIである。<br> <br> WebブラウザはHTMLを解析し、各要素をノードオブジェクトとして表現した木構造 (DOMツリー) をメモリ上に構築する。<br> JavaScriptはこのDOMツリーにアクセスし、要素の取得・作成・追加・削除、属性やスタイルの変更、ツリーの走査といった操作を行う。<br> <br> 主要なDOM操作は以下の通りである。<br> <br> <center> {| class="wikitable" |+ DOM操作の主な機能一覧 ! 機能 !! 説明 |- | 要素の取得 || <code>querySelector</code> / <code>getElementById</code> 等のメソッドで、DOMツリーから特定の要素を取得する。 |- | 要素の作成・追加・削除 || <code>createElement</code> で要素を生成し、<code>append</code> / <code>remove</code> 等でDOMツリーを変更する。 |- | テキスト・HTMLコンテンツの操作 || <code>textContent</code> / <code>innerHTML</code> 等で要素のコンテンツを読み書きする。 |- | 属性・スタイル操作 || <code>setAttribute</code> / <code>classList</code> / <code>style</code> 等でHTML属性やCSSを操作する。 |- | DOM走査 || <code>parentElement</code> / <code>children</code> / <code>closest</code> 等でツリーを移動する。 |} </center> <br> <u>なお、React等のUIフレームワークは仮想DOMを介して実際のDOM操作を最小限に抑えるため、開発者がDOMを直接操作することは原則として無い。</u><br> <br> バニラJavaScriptにおけるDOM操作の理解は、フレームワークの動作原理を把握する上でも重要である。<br> <br><br> == DOMの基本 == ==== DOMツリー ==== WebブラウザはHTMLドキュメントを読み込むと、その構造をメモリ上にツリー状のオブジェクト (DOMツリー) として構築する。<br> このDOMツリーは、HTML要素の親子関係・兄弟関係をそのまま反映している。<br> <br> 例えば、以下に示すようなHTMLがあるとする。<br> <br> <syntaxhighlight lang="html"> <html> <head> <title>ページタイトル</title> </head> <body> <h1>見出し</h1> <p>段落テキスト</p> </body> </html> </syntaxhighlight> <br> このHTMLは、以下に示すようなDOMツリーに変換される。<br> <br> Document └── html (ELEMENT_NODE) ├── head (ELEMENT_NODE) │ └── title (ELEMENT_NODE) │ └── "ページタイトル" (TEXT_NODE) └── body (ELEMENT_NODE) ├── h1 (ELEMENT_NODE) │ └── "見出し" (TEXT_NODE) └── p (ELEMENT_NODE) └── "段落テキスト" (TEXT_NODE) <br> DOMツリーの各要素はノードと呼ばれ、JavaScriptからアクセスおよび操作が可能である。<br> <br> ==== ノードの種類 ==== DOMツリーの各ノードは <code>nodeType</code> プロパティを持ち、ノードの種類を数値で表す。<br> <br> 下表に、主なノード型を示す。<br> <br> <center> {| class="wikitable" |+ 主なノード型 ! nodeType値 !! 定数名 !! 説明 |- | 1 || ELEMENT_NODE || HTML要素ノード (<code><div></code>, <code><p></code> 等) |- | 3 || TEXT_NODE || テキストノード (要素内のテキスト内容) |- | 8 || COMMENT_NODE || コメントノード (<code><!-- ... --></code>) |- | 9 || DOCUMENT_NODE || ドキュメントノード (<code>document</code> オブジェクト) |- | 10 || DOCUMENT_TYPE_NODE || DOCTYPE宣言ノード |- | 11 || DOCUMENT_FRAGMENT_NODE || ドキュメントフラグメントノード |} </center> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('p') console.log(element.nodeType) // 1 (ELEMENT_NODE) console.log(element.firstChild.nodeType) // 3 (TEXT_NODE) console.log(element.nodeName) // "P" console.log(element.firstChild.nodeValue) // テキスト内容 </syntaxhighlight> <br> 通常のDOM操作では、要素ノード (ELEMENT_NODE) を主に扱うが、テキストノードやコメントノードの存在も理解しておく必要がある。<br> <br><br> == 要素の取得 == DOMツリーから特定の要素を取得するためのメソッドが複数存在する。<br> <br> ==== querySelector / querySelectorAll ==== <code>querySelector</code> と <code>querySelectorAll</code> は、CSSセレクタを使用して要素を取得するメソッドである。<br> 柔軟なセレクタ指定が可能であり、現在最も推奨される取得方法である。<br> <br> <code>querySelector</code> は、セレクタに一致する最初の要素を返す。<br> 一致する要素がない場合は <u>null</u> を返す。<br> <br> <syntaxhighlight lang="javascript"> // IDで取得 const header = document.querySelector('#header') // クラスで取得 const item = document.querySelector('.item') // 複合セレクタ const input = document.querySelector('div.form-group input[type="text"]') // 擬似クラス const firstItem = document.querySelector('li:first-child') </syntaxhighlight> <br> <code>querySelectorAll</code> は、セレクタに一致する全ての要素を静的な <u>NodeList</u> として返す。<br> 一致する要素がない場合は空の <u>NodeList</u> を返す。<br> <br> <syntaxhighlight lang="javascript"> // 全ての段落を取得 const paragraphs = document.querySelectorAll('p') // 複数セレクタ (カンマ区切り) const elements = document.querySelectorAll('div.note, div.alert') // forEachで反復処理 paragraphs.forEach(p => { console.log(p.textContent) }) </syntaxhighlight> <br> <u><code>querySelectorAll</code> が返す <u>NodeList</u> は静的 (non-live) であり、取得後にDOMが変更されても <u>NodeList</u> の内容は更新されない。</u><br> <u><code>forEach</code> メソッドが使用可能であるため、反復処理が容易である。</u><br> <br> ==== getElementById / getElementsByClassName / getElementsByTagName ==== これらは従来から存在する要素取得メソッドである。<br> <br> <code>getElementById</code> は、指定されたIDに一致する単一の要素を返す。<br> <code>document</code> オブジェクトでのみ使用可能であり、一致する要素がない場合は <u>null</u> を返す。<br> <br> <syntaxhighlight lang="javascript"> const element = document.getElementById('main-content') </syntaxhighlight> <br> <code>getElementsByClassName</code> と <code>getElementsByTagName</code> は、動的な <u>HTMLCollection</u> を返す。<br> <br> <syntaxhighlight lang="javascript"> // クラス名で取得 (動的HTMLCollection) const items = document.getElementsByClassName('item') // 複数クラスを指定 (両方を持つ要素のみ) const activeItems = document.getElementsByClassName('item active') // タグ名で取得 (動的HTMLCollection) const paragraphs = document.getElementsByTagName('p') // 特定要素の子孫から検索 const container = document.getElementById('container') const buttons = container.getElementsByTagName('button') </syntaxhighlight> <br> <u>動的HTMLCollectionは、DOMの変更に追従して自動的に更新される。</u><br> <u>そのため、反復中にDOMを追加・削除すると無限ループ等の予期しない動作が発生する可能性がある。</u><br> <u>反復処理を行う場合は、<code>Array.from()</code> で静的なコピーを作成することを推奨する。</u><br> <br> <syntaxhighlight lang="javascript"> // 動的HTMLCollectionでの反復は注意が必要 const items = document.getElementsByClassName('item') // Array.from()で静的コピーを作成して安全に反復 Array.from(items).forEach(item => { // DOM操作を安全に行える }) </syntaxhighlight> <br> ==== 取得メソッドの比較 ==== 各取得メソッドの特性を以下に示す。<br> <br> <center> {| class="wikitable" |+ 要素取得メソッドの比較 ! メソッド !! 引数 !! 戻り値 !! 動的 / 静的 |- | <code>querySelector</code> || CSSセレクタ || Element / null || - |- | <code>querySelectorAll</code> || CSSセレクタ || NodeList || 静的 |- | <code>getElementById</code> || ID文字列 || Element / null || - |- | <code>getElementsByClassName</code> || クラス名 || HTMLCollection || 動的 |- | <code>getElementsByTagName</code> || タグ名 || HTMLCollection || 動的 |} </center> <br> <u>新規開発では <code>querySelector</code> / <code>querySelectorAll</code> の使用を推奨する。</u><br> <u>CSSセレクタによる柔軟な指定が可能であり、静的な <u>NodeList</u> を返すため安全に操作できる。</u><br> <br><br> == 要素の作成・追加・削除 == ==== 要素の作成 ==== 新しいDOM要素を作成するには、<code>document.createElement</code> メソッドを使用する。<br> <br> <syntaxhighlight lang="javascript"> // 要素の作成 const div = document.createElement('div') const p = document.createElement('p') const a = document.createElement('a') // 作成した要素に属性やテキストを設定 div.id = 'new-container' div.className = 'container' p.textContent = '新しい段落のテキスト' a.href = 'https://example.com' a.textContent = 'リンクテキスト' </syntaxhighlight> <br> テキストノードを独立して作成する場合は、<code>document.createTextNode</code> を使用する。<br> <br> <syntaxhighlight lang="javascript"> const textNode = document.createTextNode('テキスト内容') </syntaxhighlight> <br> <u><code>createElement</code> で作成された要素はメモリ上に存在するのみであり、DOMツリーに追加しない限りページ上には表示されない。</u><br> <br> ==== 要素の追加 ==== ===== 新しいAPI (append / prepend / before / after) ===== ES2015以降で追加された新しいAPIでは、複数の引数を受け取り、文字列も直接追加できる。<br> <br> <center> {| class="wikitable" |+ 新しい追加メソッド ! メソッド !! 挿入位置 !! 説明 |- | <code>append()</code> || 子要素の末尾 || 最後の子要素として追加する |- | <code>prepend()</code> || 子要素の先頭 || 最初の子要素として追加する |- | <code>before()</code> || 要素の前 || 兄弟要素として前に追加する |- | <code>after()</code> || 要素の後 || 兄弟要素として後に追加する |} </center> <br> <syntaxhighlight lang="javascript"> const parent = document.querySelector('#container') const newElement = document.createElement('p') newElement.textContent = '新しい要素' // 最後の子要素として追加 parent.append(newElement) // 文字列を直接追加 (自動的にTextNodeに変換) parent.append('テキスト') // 複数の要素とテキストを一度に追加 parent.append(newElement, 'テキスト', document.createElement('br')) // 最初の子要素として追加 parent.prepend(newElement) // 兄弟として前後に追加 const ref = document.querySelector('#reference') ref.before(newElement) ref.after(newElement) </syntaxhighlight> <br> ===== 従来のAPI (appendChild / insertBefore) ===== 従来のAPIは <code>Node</code> オブジェクトのみを引数として受け取る。<br> <br> <syntaxhighlight lang="javascript"> const parent = document.querySelector('#container') const child = document.createElement('div') // 最後の子ノードとして追加 parent.appendChild(child) // 参照ノードの前に挿入 const ref = document.querySelector('#reference') parent.insertBefore(child, ref) </syntaxhighlight> <br> 新旧APIの違いを以下に示す。<br> <br> <center> {| class="wikitable" |+ 新旧追加APIの比較 ! 項目 !! 新しいAPI (append等) !! 従来のAPI (appendChild等) |- | 文字列対応 || 可能 (自動的にTextNodeに変換) || Nodeオブジェクトのみ |- | 複数引数 || 対応 || 単一引数のみ |- | 戻り値 || <code>undefined</code> || 追加されたNode |} </center> <br> ==== 要素の削除 ==== 要素をDOMツリーから削除するメソッドを以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // remove() : 要素自身をDOMから削除 const element = document.querySelector('#target') element.remove() // removeChild() : 子ノードを親から削除 (従来のAPI) const parent = document.querySelector('#container') const child = document.querySelector('#target') parent.removeChild(child) </syntaxhighlight> <br> <code>remove()</code> は要素自身を直接削除でき、親要素の参照が不要であるため簡潔に記述できる。<br> <br> ==== DocumentFragment ==== 多数の要素をDOMに追加する場合、1つずつ追加するとその都度ブラウザのリフロー (レイアウト再計算) が発生し、パフォーマンスが低下する。<br> <code>DocumentFragment</code> を使用することにより、複数の要素をまとめて1回のDOM更新で追加できる。<br> <br> <syntaxhighlight lang="javascript"> const list = document.querySelector('ul') const fragment = document.createDocumentFragment() for (let i = 0; i < 1000; i++) { const item = document.createElement('li') item.textContent = `Item ${i}` fragment.appendChild(item) // DocumentFragmentに追加 (リフローなし) } list.appendChild(fragment) // 1回のDOM更新で全要素を追加 </syntaxhighlight> <br> <code>append</code> メソッドの複数引数を利用する方法もある。<br> <br> <syntaxhighlight lang="javascript"> const list = document.querySelector('ul') const items = [] for (let i = 0; i < 1000; i++) { const item = document.createElement('li') item.textContent = `Item ${i}` items.push(item) } list.append(...items) // スプレッド構文で一度に追加 </syntaxhighlight> <br><br> == テキスト・HTMLコンテンツの操作 == ==== textContent / innerText / innerHTML ==== 要素のテキストやHTMLコンテンツを操作するプロパティが3つ存在する。<br> それぞれの特性が異なるため、用途に応じた使い分けが重要である。<br> <br> <center> {| class="wikitable" |+ テキスト・HTMLコンテンツ操作プロパティの比較 ! 項目 !! textContent !! innerText !! innerHTML |- | 用途 || プレーンテキストの取得 / 設定 || 表示テキストの取得 / 設定 || HTMLマークアップの取得/設定 |- | 非表示要素の扱い || 取得する || 取得しない (CSSを考慮) || HTMLとして取得する |- | script / style要素 || テキストとして取得する || 取得しない || HTMLとして取得する |- | XSSリスク || 安全 || 安全 || 危険 |- | パフォーマンス || 高速 || 低速 (CSSレイアウト計算が必要) || HTMLパーサーの呼び出しが必要 |} </center> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('#content') // textContent: プレーンテキストとして取得 / 設定 const text = element.textContent element.textContent = '新しいテキスト' // innerText: 表示されているテキストのみ取得 / 設定 const visibleText = element.innerText // innerHTML: HTMLマークアップを含めて取得 / 設定 const html = element.innerHTML element.innerHTML = '<p>新しい<strong>HTML</strong>コンテンツ</p>' </syntaxhighlight> <br> <u><code>textContent</code> を設定すると、要素の全ての子ノードが削除され、単一のテキストノードに置き換わる。</u><br> <br> <u><code>innerHTML</code> はXSS (クロスサイトスクリプティング) 攻撃の脆弱性となるため、ユーザ入力を含む文字列を設定してはならない。</u><br> <u>テキストの設定には、<code>textContent</code> を使用すること。</u><br> <br> <syntaxhighlight lang="javascript"> // ユーザ入力をinnerHTMLに設定するのは危険 const userInput = '<img src="x" onerror="alert(\'XSS\')">' element.innerHTML = userInput // スクリプトが実行される // textContentを使用すればプレーンテキストとして安全に設定される element.textContent = userInput // そのまま文字列として表示される </syntaxhighlight> <br> ==== insertAdjacentHTML ==== <code>insertAdjacentHTML</code> は、指定したHTML文字列をDOMツリーの指定位置に挿入するメソッドである。<br> 既存の要素を破壊せずにHTMLを挿入できるため、<u>innerHTML</u> による再設定より効率的である。<br> <br> 4つの挿入ポジションを以下に示す。<br> <br> <center> {| class="wikitable" |+ insertAdjacentHTMLのポジション ! ポジション !! 挿入位置 |- | <code>beforebegin</code> || 要素自身の直前 (兄弟として) |- | <code>afterbegin</code> || 要素の先頭 (最初の子要素の前) |- | <code>beforeend</code> || 要素の末尾 (最後の子要素の後) |- | <code>afterend</code> || 要素自身の直後 (兄弟として) |} </center> <br> <syntaxhighlight lang="html"> <!-- beforebegin --> <p> <!-- afterbegin --> content <!-- beforeend --> </p> <!-- afterend --> </syntaxhighlight> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('#target') element.insertAdjacentHTML('beforebegin', '<div>要素の前</div>') element.insertAdjacentHTML('afterbegin', '<span>先頭に挿入</span>') element.insertAdjacentHTML('beforeend', '<span>末尾に挿入</span>') element.insertAdjacentHTML('afterend', '<div>要素の後</div>') </syntaxhighlight> <br> <u><code>insertAdjacentHTML</code> もHTMLを解析するため、ユーザ入力の挿入にはXSSのリスクがある。</u><br> <u>プレーンテキストの挿入には <code>insertAdjacentText</code> を使用すること。</u><br> <br><br> == 属性操作 == ==== 標準的な属性操作 ==== 下表に、要素の属性を操作する基本的なメソッドを示す。<br> <br> <center> {| class="wikitable" |+ 属性操作メソッド ! メソッド !! 説明 |- | <code>getAttribute(name)</code> || 指定した属性の値を文字列で取得する。 |- | <code>setAttribute(name, value)</code> || 属性を設定する。(存在しない場合は新規作成) |- | <code>hasAttribute(name)</code> || 属性が存在するかを <u>boolean</u> で返す。 |- | <code>removeAttribute(name)</code> || 属性を削除する。 |} </center> <br> <syntaxhighlight lang="javascript"> const link = document.querySelector('a') // 属性の取得 const href = link.getAttribute('href') // 属性の設定 link.setAttribute('href', 'https://example.com') link.setAttribute('target', '_blank') // 属性の存在確認 if (link.hasAttribute('target')) { console.log('target属性が存在する') } // 属性の削除 link.removeAttribute('target') // boolean属性 (disabled, readonly等) の設定 const button = document.querySelector('button') button.setAttribute('disabled', '') // 無効化 button.removeAttribute('disabled') // 有効化 </syntaxhighlight> <br> ==== data属性 (dataset) ==== HTML5の <code>data-*</code> カスタムデータ属性は、<code>dataset</code> プロパティを使用してアクセスする。<br> 属性名は、ケバブケースからキャメルケースに自動変換される。<br> <br> <center> {| class="wikitable" |+ ケバブケース と キャメルケースの比較 ! 項目 !! ケバブケース (kebab-case) !! キャメルケース (camelCase / PascalCase) |- ! 区切り文字 | ハイフン `-` || なし(大文字で区切る) |- ! 先頭文字 | 小文字 || 小文字(lowerCamelCase)または大文字(UpperCamelCase / PascalCase) |- ! 記述例 | my-variable-name || myVariableName / MyVariableName |- ! 主な用途 | CSS クラス名・ID、URL スラッグ、HTML 属性、ファイル名 || 変数名・メソッド名(Java, C#, JavaScript 等)、クラス名(PascalCase) |- ! 言語・環境の例 | HTML, CSS, Lisp 系, REST APIのエンドポイント || C#, Java, JavaScript, TypeScript, Swift |- ! 大文字・小文字の区別 | 全て小文字が基本 || 単語の先頭を大文字にする。 |- ! スペースの代替 | ハイフンがスペース相当 || 大文字がスペース相当 |- ! 視認性 | ハイフンにより単語の区切りが明確 || 慣れるまで単語の区切りが読みにくい場合がある。 |- ! URLでの使用 | 使用可能(推奨) || 非推奨(大文字が小文字に正規化される場合がある) |} </center> <br> <syntaxhighlight lang="html"> <div id="user" data-user-id="123" data-user-name="John" data-date-of-birth="1990-01-01"> John </div> </syntaxhighlight> <br> <syntaxhighlight lang="javascript"> const el = document.querySelector('#user') // 読み取り (ケバブケース → キャメルケース) console.log(el.dataset.userId) // "123" (data-user-id) console.log(el.dataset.userName) // "John" (data-user-name) console.log(el.dataset.dateOfBirth) // "1990-01-01" (data-date-of-birth) // 設定 el.dataset.role = 'admin' // data-role="admin" が追加される // 削除 delete el.dataset.dateOfBirth // 存在確認 if ('userId' in el.dataset) { console.log('userId属性が存在する') } </syntaxhighlight> <br> <center> {| class="wikitable" |+ data属性の名前変換規則 ! HTML属性名 !! JavaScriptプロパティ名 |- | data-id || dataset.id |- | data-user-name || dataset.userName |- | data-date-of-birth || dataset.dateOfBirth |} </center> <br> ==== クラス操作 (classList) ==== <code>classList</code> プロパティは、要素のCSSクラスを操作するためのメソッドを提供する。<br> <br> <center> {| class="wikitable" |+ classListのメソッド ! メソッド !! 説明 |- | <code>add(class1, class2, ...)</code> || 1つ以上のクラスを追加する。 |- | <code>remove(class1, class2, ...)</code> || 1つ以上のクラスを削除する。 |- | <code>toggle(class)</code> || クラスが存在すれば削除し、存在しなければ追加する。 |- | <code>toggle(class, force)</code> || <code>force</code> が <code>true</code> なら追加、<u>false</u> なら削除する。 |- | <code>contains(class)</code> || クラスが存在するかを <u>boolean</u> で返す |- | <code>replace(oldClass, newClass)</code> || クラスを別のクラスに置換する |} </center> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('#target') // クラスの追加 element.classList.add('active') element.classList.add('highlight', 'visible') // 複数クラスを一度に追加 // クラスの削除 element.classList.remove('active') element.classList.remove('highlight', 'visible') // クラスのトグル element.classList.toggle('active') // 追加 / 削除を切り替え element.classList.toggle('active', isActive) // 条件に基づいて追加 / 削除 // クラスの存在確認 if (element.classList.contains('active')) { console.log('activeクラスが存在する') } // クラスの置換 element.classList.replace('old-class', 'new-class') </syntaxhighlight> <br> ==== スタイル操作 ==== 要素のスタイルを操作する方法として、<code>element.style</code> プロパティと <code>getComputedStyle</code> 関数がある。<br> <br> <code>element.style</code> はインラインスタイル (<code>style</code> 属性) のみを操作する。<br> CSSプロパティ名はケバブケースからキャメルケースに変換して使用する。<br> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('#target') // スタイルの設定 (ケバブケース -> キャメルケース) element.style.color = 'blue' element.style.fontSize = '18px' // font-size element.style.backgroundColor = '#f0f0f0' // background-color element.style.borderTopWidth = '2px' // border-top-width // スタイルの削除 (空文字列を設定) element.style.color = '' </syntaxhighlight> <br> <code>getComputedStyle</code> は、外部スタイルシートを含む全てのCSSプロパティの計算済み値を取得する読み取り専用の関数である。<br> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('#target') const styles = window.getComputedStyle(element) // 計算済みスタイルの取得 const fontSize = styles.getPropertyValue('font-size') const color = styles.getPropertyValue('color') console.log(fontSize) // "16px" (外部CSSも含めた実際の値) // 擬似要素のスタイル取得 const afterStyles = window.getComputedStyle(element, '::after') const content = afterStyles.getPropertyValue('content') </syntaxhighlight> <br> <center> {| class="wikitable" |+ element.style と getComputedStyle の比較 ! 項目 !! element.style !! getComputedStyle() |- | 対象範囲 || インラインスタイルのみ || 全てのCSSプロパティ (外部スタイル含む) |- | 読み書き || 読み書き可能 || 読み取り専用 |- | 値の種類 || インラインスタイルの値 || Webブラウザが計算した最終的な値 |} </center> <br> <u>視覚的なスタイル変更は <code>classList</code> によるCSSクラスの切り替えが推奨される。</u><br> <u><code>element.style</code> は動的に計算が必要な値 (アニメーションの座標等) の設定に適している。</u><br> <br><br> == DOM走査 == ==== 親・子・兄弟要素のアクセス ==== DOMツリーを移動するためのプロパティとして、Element系とNode系の2種類がある。<br> <br> Element系はHTML要素ノードのみを対象とし、Node系はテキストノードやコメントノードを含む全てのノードを対象とする。<br> <br> <center> {| class="wikitable" |+ DOM走査プロパティの比較 ! 操作 !! Element系 (要素のみ) !! Node系 (全ノード) |- | 親 || parentElement || parentNode |- | 子 (コレクション) || children (HTMLCollection) || childNodes (NodeList) |- | 最初の子 || firstElementChild || firstChild |- | 最後の子 || lastElementChild || lastChild |- | 次の兄弟 || nextElementSibling || nextSibling |- | 前の兄弟 || previousElementSibling || previousSibling |} </center> <br> <syntaxhighlight lang="javascript"> const element = document.querySelector('#target') // 親要素 const parent = element.parentElement // 子要素 const children = element.children // HTMLCollection (要素のみ) const firstChild = element.firstElementChild // 最初の子要素 const lastChild = element.lastElementChild // 最後の子要素 // 兄弟要素 const next = element.nextElementSibling // 次の兄弟要素 const prev = element.previousElementSibling // 前の兄弟要素 </syntaxhighlight> <br> <u>HTML内の改行やスペースはテキストノードとして解析されるため、Node系のプロパティ (<code>firstChild</code> 等) では予期しないテキストノードが取得される場合がある。</u><br> <u>通常は、Element系のプロパティ (<code>firstElementChild</code> 等) の使用を推奨する。</u><br> <br> ==== closest ==== <code>closest</code> メソッドは、指定したCSSセレクタに一致する最も近い祖先要素 (またはその要素自身) を返す。<br> 要素自身から始まり、DOMツリーを上方向に走査する。<br> <br> 一致する要素がない場合は <u>null</u> を返す。<br> <br> <syntaxhighlight lang="javascript"> // HTML : <article> > <div id="outer"> > <div id="inner"> > <p id="target"> const target = document.querySelector('#target') target.closest('div') // <div id="inner"> (最も近いdiv) target.closest('#outer') // <div id="outer"> target.closest('article') // <article> target.closest('section') // null (一致なし) target.closest(':not(div)') // <article> (divでない最も近い祖先) </syntaxhighlight> <br> <code>closest</code> はイベント委譲 (Event Delegation) で頻繁に使用されるメソッドである。<br> イベント委譲の詳細については[[JavaScriptの基礎 - イベント(DOM)]]を参照すること。<br> <br> <syntaxhighlight lang="javascript"> // イベント委譲での典型的なclosestの使用例 document.addEventListener('click', event => { const button = event.target.closest('button') if (button) { // ボタンまたはその子要素がクリックされた場合の処理 console.log(button.dataset.action) } }) </syntaxhighlight> <br><br> == ReactがDOMを直接操作しない理由 == React等のモダンフレームワークでは、開発者がDOMを直接操作することは原則としてない。<br> <br> この方針には、いくつかの重要な理由がある。<br> <br> ==== 仮想DOM ==== Reactは仮想DOMと呼ばれる仕組みを採用している。<br> 仮想DOMはDOMの軽量なインメモリ表現であり、実際のDOM操作の前に変更差分を計算して、最小限の実DOM更新のみを実行する。<br> <br> この仕組みにより、開発者は <u>UIがどのような状態であるべきか</u> を宣言的に記述するだけでよく、実際のDOM操作はReactが最適化して処理する。<br> <br> ==== 命令型と宣言型の対比 ==== 直接DOM操作は命令型のアプローチである。<br> <u>何をどのように変更するか</u> をステップごとに記述する必要があり、状態が増えるほどUIとの同期が複雑化する。<br> <br> 一方、Reactは宣言型のアプローチを採用しており、<u>この状態のときにUIはこうあるべき</u> と記述する。<br> 状態が変化すると、Reactが自動的にDOMの差分を計算して更新する。<br> <br> ==== 直接DOM操作の問題点 ==== 大規模なアプリケーションで直接DOM操作を行う場合、以下に示すような問題が発生しやすい。<br> <br> * 状態の不整合 *: JavaScriptの変数が保持する状態とUIの表示が乖離するリスクがある。 *: 複数箇所からDOMを操作すると、整合性の維持が困難になる。 *: <br> * パフォーマンスの非効率性 *: 不必要なDOM操作やリフロー・リペイントの増加が発生しやすい。 *: 個々の操作では最適化が困難である。 *: <br> * 保守性の低下 *: 状態変更とUI更新の関連性が把握しにくくなる。 *: コードの複雑化に伴い、バグが増加する傾向がある。 <br> ただし、React開発においても <code>ref</code> を使用して直接DOM要素にアクセスする場面 (フォーカス管理、スクロール制御、外部ライブラリとの統合等) は存在する。<br> そのため、DOMの基本操作を理解しておくことは重要である。<br> <br><br> == 関連情報 == * [[JavaScriptの基礎 - イベント(DOM)]] <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - DOM操作
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse