DOMの基本構造
スポンサーリンク
DOMではDocument、Nodeなどのインターフェイスを元に文書(ドキュメント)を定義する構造であり、いくつかのインターフェイスが子要素を持つことで木構造を成します。
DOMのツリー構造
例えば以下のようなXMLを考えます。
[XML] <root myAttr1="attr" myAttr2="foo"> <myElement myAttr3="val" myAttr4="hoge">text</myElement> <myElement2 /> </root>
このXMLをDOMに当てはめると以下の構造が考えられます。(改行やインデントによるスペースのTextノードは省略します)
- #document [Document]
- documentElement※ <root> [Element]
- attributes [NamedNodeMap]
- [0] myAttr1="attr" [Attr]
- [1] myAttr2="foo" [Attr]
- childNodes [NodeList]
- [0] <myElement> [Element]
- attributes [NamedNodeMap]
- [0] myAttr3="val" [Attr]
- [1] myAttr4="hoge" [Attr]
- childNodes [NodeList]
- [0] "text" [Text]
- attributes [NamedNodeMap]
- [1] <myElement2> [Element]
- [0] <myElement> [Element]
- attributes [NamedNodeMap]
- documentElement※ <root> [Element]
※ documentElementの定義は「親がdocumentである要素」であり、他にNodeが無ければchildNodes[0]と同義です(ブラウザーがサポートしていればfirstElementChildと同じです)。
基本的には、XMLのタグはすべて「Element」となり、Elementに定義される属性(Attr)のまとまりは「attributes」、子要素(Element、Text、CDATASectionなど)のまとまりは「childNodes」に入ります。また、XMLやHTMLなどは必ず「Document」がトップレベルの親となります。なお、Documentインターフェイスが持つ子要素としてのElement・DocumentTypeは最大1つまでと定められています。
※ DocumentTypeの制限は「DOM Level2 Core Specification」より定義。
また、ElementやAttributeなど、多くのインターフェイスはNodeインターフェイスから派生しています。以下はインターフェイスの派生状況をツリー表示したものです(一部のみ掲載しています)。
- Node
- CharacterData
- Comment
- Text
- CDATASection※2
- ProcessingInstruction※3
- Document
- DocumentFragment
- DocumentType
- Element
- Attr※4
- Entity※2
- EntityReference※2
- Notation※2
- CharacterData
- NodeList
- NamedNodeMap
- DOMImplementation
- DOMError※1
- DOMStringList※1※2
- NodeIterator※5
- TreeWalker※5
- NodeFilter※5
- DOMTokenList※5
- DOMSettableTokenList※5
※1 これらのインターフェイスは「DOM Level 3 Core Specification」(以下DOM3)で新たに登場したインターフェイスです。
※2 これらのインターフェイスはDOM4にて削除されています。
※3 ProcessingInstructionインターフェイスはDOM3まではNodeインターフェイスを直接継承していましたが、DOM4ではTextインターフェイスを直接継承する形になっています。
※4 AttrインターフェイスはDOM4では独立したインターフェイスに変更となっていますが、DOM3まで、およびDOM4.1のDraft(2018/02/01)時点ではNodeインターフェイスを継承しています。
※5 これらのインターフェイスはDOM4で新たに登場したインターフェイスです。
スポンサーリンク
Nodeの子要素を調べる
上記のように、ElementやTextの要素はNodeインターフェイスのプロパティやメソッドを使用できます。そのため、childNodesを使ってNodeの一覧を取得した場合、その子要素がElementやTextであるかなどを判定する場合は、Nodeで定義されている「nodeType」を使って判定することができます。(逆に、childNodesの中身はすべてNodeとなっているので、型に厳密な言語では目的の型に適切に変換を行う必要があります。)
[JavaScript] var element, node; // element には既に Element の参照が入っているとする for (var i = 0; i < element.childNodes.length; i++) { node = element.childNodes[i]; if (node.nodeType === 1) { // 1 == ELEMENT_NODE // node は Element なので tagName などが使える } else if (node.nodeType === 3) { // 3 == TEXT_NODE // node は Text なので nodeValue にテキストが入っている } ... }
[VB.NET] (System.Xml ライブラリ使用) Imports System.Xml Dim element As XmlElement, node As XmlNode ' element には既に XmlElement の参照が入っているとする For Each node In element.ChildNodes Select Case node.NodeType Case XmlNodeType.Element ' node は XmlElement に変換して Attributes などが使用可能 ' (※ TagName プロパティは存在しないので Name を使う) Case XmlNodeType.Text ' node は XmlText であり、(node.)Value プロパティにはテキストが入っている ... End Select Next node
※ 言語や環境によって、インスタンスの型チェック(JavaScriptではinstanceof演算子、VB.NETではTypeOf演算子)を使ってノードの種類を調べることもできます。ただし一部のブラウザー上では「Element」などの名前が利用できない場合があるため、それらを意識する場合はnodeTypeでの判定を行う方が確実です。
※ DOM4にて、NodeListはiterable<Node>の宣言が付加されるようになりました。これはentries、forEach、keys、およびvaluesの各メソッド(および@@iterator)が使用できるということを表しています。ただしDOM4に対応していないような一部のブラウザーでは利用できない可能性が高いため注意が必要です。
属性を表す「Attr」はNodeの子要素としては現れず、代わりにElement(DOM3まではNode)に定義されているattributesを用います。attributesは、通常の配列を表すNodeListインターフェイスの代わりに、名前付き配列(マップ)であるNamedNodeMapインターフェイスが使用されています。したがって、「myAttr1」という属性を取得したい場合は
[JavaScript] var attr = element.attributes.getNamedItem("myAttr1");
のように「getNamedItem」メソッドでAttrインターフェイスを取得することができます。
※ 上記の例で「myAttr1」が「element」の属性として定義されていない場合は、attrにnullが返ります。また、(DOM3までは)elementがElementでない場合はattributesがnullとなるためエラーになります。(仕様)
※ NamedNodeMap(attributesを含む)はNodeListではありません。そのためDOM4やDOM4.1(2018/02/01時点)においてもforEachなどを使用することはできません。
※ DOM4時点での仕様では、NamedNodeMapの「getNamedItem」メソッドはNodeを返すことになっていますが、AttrはNodeでは無くなっているため、この定義は厳密には誤りになり、DOM4.1で修正される予定です(参考: [w3c/dom] Attr and NamedNodeMap in DOM4 are inconsistent - Issue #4)。
最終更新日: 2018/02/20