コンテンツにスキップ

Document Object Model

出典: フリー教科書『ウィキブックス(Wikibooks)』

Document Object Model (DOM) は、HTMLやXML文書をプログラムから操作するための標準的なインターフェースです。重要な点として、DOMはJavaScriptに限定されたものではなく、言語に依存しない仕様として設計されています。

基本概念

[編集]

DOMツリー

[編集]

DOМは文書を「ノード」の木構造として表現します。主なノードの種類:

  • Document - 文書全体を表すルートノード
  • Element - HTML要素
  • Attribute - 要素の属性
  • Text - テキストコンテンツ
  • Comment - コメント
コード例(JavaScript)
// 要素の取得
const element = document.querySelector('#example');
// 子要素へのアクセス
const children = element.childNodes;
// 親要素へのアクセス
const parent = element.parentNode;

要素の操作

[編集]

要素の検索

[編集]
コード例
// ID指定
const element = document.querySelector('#myId');
// クラス指定
const elements = document.querySelectorAll('.myClass');
// CSSセレクタ
const selected = document.querySelector('.myClass > p');

要素の作成と追加

[編集]
コード例(JavaScript)
// 新しい要素の作成
const newDiv = document.createElement('div');
// テキストの設定
newDiv.textContent = 'Hello, DOM!';
// 属性の設定
newDiv.setAttribute('class', 'myClass');
// DOMツリーへの追加
document.body.appendChild(newDiv);
コード例(Java)
import org.w3c.dom.*;

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();

// 要素の作成
Element div = doc.createElement("div");
// テキストの設定
div.setTextContent("Hello, DOM!");
// 属性の設定
div.setAttribute("class", "myClass");

イベント処理

[編集]
コード例(JavaScript)
// イベントリスナーの追加
element.addEventListener('click', function(event) {
    console.log('要素がクリックされました');
});

// イベントの削除
const handler = function(event) {
    // 処理
};
element.removeEventListener('click', handler);

属性の操作

[編集]
コード例
// 属性の取得
const value = element.getAttribute('data-value');
// 属性の設定
element.setAttribute('class', 'newClass');
// 属性の削除
element.removeAttribute('style');
// 属性の存在確認
if (element.hasAttribute('id')) {
    // 処理
}

DOM操作のベストプラクティス

[編集]

パフォーマンスの最適化

[編集]
  • DOM操作は最小限に抑える
  • DocumentFragmentの使用
  • 要素の再利用
  • バッチ処理の活用
コード例
// DocumentFragmentの使用例
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
    const div = document.createElement('div');
    div.textContent = `Item ${i}`;
    fragment.appendChild(div);
}
document.body.appendChild(fragment);

クロスブラウザ対応

[編集]
  • 標準的なDOM APIの使用
  • ポリフィルの活用
  • ブラウザ固有の機能の確認

高度なDOM操作

[編集]

DOM Range

[編集]

テキストや要素の範囲を操作するためのAPI

コード例
const range = document.createRange();
const selection = window.getSelection();
const paragraph = document.querySelector('p');

range.selectNodeContents(paragraph);
selection.removeAllRanges();
selection.addRange(range);

DOM Mutation Observer

[編集]

DOM変更の監視

コード例
const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
        console.log('DOM変更:', mutation.type);
    });
});

observer.observe(targetNode, {
    childList: true,
    attributes: true,
    subtree: true
});

セキュリティの考慮事項

[編集]

クロスサイトスクリプティング(XSS)対策

[編集]
  • textContent の使用(innerHTML の代わりに)
  • 入力値のサニタイズ
  • Content Security Policy (CSP) の実装
コード例
// 安全なテキスト挿入
element.textContent = userInput;

// 危険な例(使用を避ける)
element.innerHTML = userInput;

DOM標準の沿革

[編集]

誕生の背景(1995-1997)

[編集]

DOMの標準化以前、ブラウザ各社は独自のAPIを実装していました:

  • Netscape NavigatorのDocument API
  • Internet ExplorerのDocument All コレクション

これらの互換性の無さが、Web開発者に大きな困難をもたらしていました。

Level 0 DOM

[編集]
  • 正式な仕様ではなく、事実上の標準
  • Netscape Navigator 3.0とInternet Explorer 3.0で実装された基本的なDOM機能
  • document.forms, document.images, document.links などの基本的なコレクション
  • 限定的な要素操作機能

Level 1 DOM(1998)

[編集]
  • W3Cによる最初の正式なDOM仕様
  • HTMLとXMLの基本的な文書構造とマニピュレーション機能を定義
  • 主な特徴:
    • 文書を階層的なノードツリーとして定義
    • 基本的なノード操作メソッド(appendChild, removeChild など)
    • 要素とその属性へのアクセス方法
コード例(Level 1の基本機能)
// Level 1 DOMの基本的な操作
const element = document.createElement('div');
const text = document.createTextNode('Hello DOM Level 1');
element.appendChild(text);
document.body.appendChild(element);

Level 2 DOM(2000)

[編集]
  • より高度なイベント処理モデルを導入
  • 名前空間のサポート
  • スタイルシートとCSSのプログラム的操作
  • DOM Viewsの導入
  • トラバーサル(文書走査)メソッドの拡張
コード例(Level 2のイベント処理)
// Level 2のイベントリスナー
element.addEventListener('click', function(e) {
    // イベントの伝播を制御
    e.stopPropagation();
    // デフォルト動作を防止
    e.preventDefault();
}, false);

Level 3 DOM(2004)

[編集]
  • XPath統合
  • キーボードイベントの標準化
  • 文書の検証とロード
  • 文書保存の機能
  • コンテンツモデルとドキュメント検証

DOM Living Standard(2015-現在)

[編集]
  • WHATWG(Web Hypertext Application Technology Working Group)による管理
  • 継続的に更新される「Living Standard」方式を採用
  • モダンなWeb APIとの統合
  • 新しい機能の継続的な追加:
    • Shadow DOM
    • Custom Elements
    • Template elements
    • MutationObserver
コード例(モダンなDOM機能)
// Shadow DOMの使用例
class CustomElement extends HTMLElement {
    constructor() {
        super();
        const shadow = this.attachShadow({mode: 'open'});
        const div = document.createElement('div');
        div.textContent = 'Shadow DOM Content';
        shadow.appendChild(div);
    }
}
customElements.define('custom-element', CustomElement);

重要な仕様の分割

[編集]

現代のDOM標準は、より小さく管理しやすい仕様に分割されています:

  • DOM Core - 基本的なDOM操作
  • DOM Events - イベント処理
  • DOM Parsing - 文書の解析とシリアライズ
  • DOM Range - テキスト範囲の操作
  • DOM Traversal - 文書走査
  • DOM XPath - XPathサポート

ブラウザ実装の変遷

[編集]
  • 1990年代後半: 各ブラウザの独自実装
  • 2000年代前半: 標準化への緩やかな対応
  • 2000年代後半: より厳密な標準準拠
  • 2010年代: モダンブラウザによる高度な機能の実装
  • 現在: 主要ブラウザ間での高い互換性

DOMの将来

[編集]
  • Web Componentsとの更なる統合
  • パフォーマンス最適化
  • セキュリティ強化
  • 新しいWeb APIとの連携
  • リアクティブプログラミングモデルとの統合

WebIDLとDOM実装

[編集]

WebIDLの役割

[編集]

WebIDL(Web Interface Definition Language)は、WebプラットフォームのAPIを形式的に定義するための言語です。主な目的:

  • インターフェース定義の標準化
  • 言語バインディングの自動生成
  • 型安全性の確保
  • API仕様の明確化

ブラウザでの実装プロセス

[編集]

主要ブラウザエンジンは、WebIDL定義からネイティブコードを生成します:

  • Chromium (Blink)
    • bindings/scripts/が変換処理を担当
    • IDLファイルから C++ コードを生成
    • V8エンジンとのバインディングも自動生成
Elementインターフェースの定義
[Exposed=Window]
interface Element : Node {
    readonly attribute DOMString? namespaceURI;
    readonly attribute DOMString? prefix;
    readonly attribute DOMString localName;
    readonly attribute DOMString tagName;

    [CEReactions] attribute DOMString id;
    [CEReactions] attribute DOMString className;
    [SameObject, PutForwards=value] readonly attribute DOMTokenList classList;
    [CEReactions, Unscopable] attribute DOMString slot;

    boolean hasAttributes();
    [SameObject] readonly attribute NamedNodeMap attributes;
    sequence<DOMString> getAttributeNames();
    DOMString? getAttribute(DOMString qualifiedName);
    DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
    [CEReactions] void setAttribute(DOMString qualifiedName, DOMString value);
    [CEReactions] void setAttributeNS(DOMString? namespace, DOMString qualifiedName, DOMString value);
    [CEReactions] void removeAttribute(DOMString qualifiedName);
    [CEReactions] void removeAttributeNS(DOMString? namespace, DOMString localName);
    boolean hasAttribute(DOMString qualifiedName);
    boolean hasAttributeNS(DOMString? namespace, DOMString localName);
};
  • Firefox (Gecko)
    • WebIDL ParserがIDLファイルを解析
    • バインディングジェネレータがC++コードを生成
    • SpiderMonkeyエンジンとの連携コードも生成
  • WebKit
    • IDLファイルからObjective-C/C++コードを生成
    • JavaScriptCoreとのバインディング生成

生成されるコードの種類

[編集]

WebIDLから生成される主なコード:

  1. インターフェース実装
    • C++/Objective-Cでの実際のクラス定義
    • メソッドとプロパティの実装
  2. バインディングレイヤー
    • JSエンジンとネイティブコード間の橋渡し
    • 型変換処理
    • 例外処理
  3. テストコード
    • インターフェース準拠テスト
    • Web Platform Testsとの統合

コード生成の例

[編集]
WebIDL定義
interface HTMLImageElement : HTMLElement {
    [HTMLConstructor] constructor();
    [CEReactions] attribute DOMString alt;
    [CEReactions] attribute DOMString src;
    [CEReactions] attribute DOMString srcset;
    [CEReactions] attribute DOMString sizes;
    [CEReactions] attribute DOMString crossOrigin;
    [CEReactions] attribute DOMString useMap;
    [CEReactions] attribute boolean isMap;
    [CEReactions] attribute unsigned long width;
    [CEReactions] attribute unsigned long height;
    readonly attribute unsigned long naturalWidth;
    readonly attribute unsigned long naturalHeight;
    readonly attribute boolean complete;
    readonly attribute DOMString currentSrc;
    [CEReactions] attribute DOMString decoding;
    [CEReactions] attribute DOMString loading;
    Promise<void> decode();
};
生成されるC++コード(概念的な例)
class HTMLImageElement : public HTMLElement {
public:
    // コンストラクタ
    static HTMLImageElement* Create(Document&);
    
    // 属性アクセサ
    String alt() const;
    void setAlt(const String&);
    
    String src() const;
    void setSrc(const String&);
    
    unsigned width() const;
    void setWidth(unsigned);
    
    // 読み取り専用プロパティ
    unsigned naturalWidth() const;
    unsigned naturalHeight() const;
    
    // メソッド
    Promise<void> decode();
    
private:
    // 実装詳細
    String m_alt;
    String m_src;
    unsigned m_width;
    // ...
};

パフォーマンスの考慮

[編集]

WebIDLからの自動生成には以下の最適化が含まれます:

  • キャッシング戦略
    • プロパティアクセスの最適化
    • メソッド呼び出しのキャッシング
  • メモリ最適化
    • 適切なメモリレイアウト
    • 仮想メソッドテーブルの最適化
  • 型変換の最適化
    • 高速な型チェック
    • 効率的な値の変換

拡張と制約

[編集]
  • 拡張属性
    • [CEReactions] - Custom Elements
    • [Exposed] - グローバルスコープ
    • [SameObject] - オブジェクト同一性
    • [PutForwards] - 転送属性
  • 制約
    • セキュリティチェック
    • 型チェック
    • 値の範囲チェック

デバッグとトレース

[編集]
  • バインディングデバッグ情報の生成
  • パフォーマンスプロファイリングのサポート
  • エラー報告メカニズム

まとめ

[編集]
Wikipedia
Wikipedia
ウィキペディアDocument Object Modelの記事があります。

DOMは、プログラミング言語に依存しない、文書操作のための強力なインターフェースです。JavaScriptだけでなく、多くのプログラミング言語でDOMを使用することができ、これによって文書の構造化された操作が可能になります。