Programming Field

DOMで要素の属性を取得・設定・削除する

スポンサーリンク

このページでは、DOMの要素における属性(attribute)の設定方法を紹介します。

[関連キーワード: getAttributesetAttributeremoveAttributegetAttributeNodesetAttributeNoderemoveAttributeNodeattributesgetAttributeNSsetAttributeNS]

属性

DOMでは、要素(Element)だけが「属性」を持つことが出来ます。DOM4以降では、Elementインターフェイスに「attributes」プロパティーが定義されており、このプロパティー経由で属性のリストを得ることができます。なお、DOM3まではElementの継承元であるNodeattributesプロパティーが定義されていますが、Element以外ではnullとなります。

attributesの中身は読み書き可能な名前付きリスト(NamedNodeMap)となっているため、属性を操作するにはこれ1つでも問題はありませんが、Elementに定義されている各種メソッドを使用して属性を操作することもできます。

属性の取得

まずは簡単な属性の取得方法です。以下のコードはいずれも同じ結果(val1 == val2 == val3)を得ることができます。

[JavaScript]

var attrNode1 = element.attributes.getNamedItem("attr1");
var val1 = attrNode1 ? attrNode1.value : null;
var val2 = element.getAttribute("attr1");
var attrNode2 = element.getAttributeNode("attr1"); // DOM4を除く
var val3 = attrNode2 ? attrNode2.value : null;

また、attributesはインデックスによる参照も可能(配列と同じ)なので、以下のように書くこともできます。(結果: val4 == val1)

[JavaScript]

var val4 = null;
for (var i = 0; i < element.attributes.length; i++) {
    var attrNode = element.attributes[i];
    if (attrNode.name === "attr1") {
        val4 = attrNode.value;
        break;
    }
}

Attrにはnamevalueプロパティーが定義されており、それぞれnodeNamenodeValueと同値です。

さらに、DOM3まではAttrは子ノードとしてTextを持つことができ、それを参照することで属性の値を取得することもできます。なお、子ノードが複数ある場合、nodeValueはそれらを連結した値になります。(結果: val5 === val1)

※ DOM4以降はAttrは子ノードを持ちません。そのため、最新のブラウザーでは以下の方法を用いることができません。

[JavaScript]

var attrNode = element.getAttributeNode("attr1");
var val5;
if (attrNode) {
    val5 = "";
    for (var i = 0; i < attrNode.childNodes.length; i++) {
        val5 += attrNode.childNodes[i].nodeValue;
    }
} else {
    val5 = null;
}

なお、属性によっては「既定値」が存在する場合があり、既定値が存在する属性はユーザー自身が属性を指定していない場合でも(あたかも既定値が指定されているかのように)attributesのリストに現れることがあります。この「既定値が使用されている」かどうかを判定するには、Attrspecifiedプロパティーを使います。ただしDOM4以降はspecifiedは常に「true」になるため、「既定値」をattributes経由で取得することはできません。

[JavaScript]

var attrNode = element.getAttributeNode("attr1");
if (!attrNode || !attrNode.specified) {
    // ユーザーは属性'attr1'を指定していない
} else {
    // ユーザーは属性'attr1'を指定している
}

※ 既定値が存在せずユーザーもその属性を指定していない場合、その属性はリストに現れず、その属性名が渡されたgetAttributeNodeメソッドはnullを返します。
specifiedの挙動はブラウザによって多少異なる模様です。

属性の値を(XMLやHTMLを記述する際に)指定する時、文字列の一部に実体参照(EntityReference)を含むことができます。この場合、valueには展開された値が入りますが、Attrの子ノード(childNodesリスト)にはEntityReferenceのインスタンスが入ります。

EntityReferenceがリストに現れる機能はブラウザー上ではMSXMLでしか機能しない模様です(DOM4以降では仕様外になっています)。

属性の設定

要素にある属性の値を設定する単純な方法として、ElementsetAttributeメソッドを使う方法があります。

[JavaScript]

element.setAttribute("attr1", "newValue");

Attrインターフェイスを介して設定する場合は、DocumentインターフェイスのcreateAttributeメソッドを使い、必要な値を設定したのち、そのインスタンスをElementsetAttributeNodeメソッドに渡します。(DOM4を除く。DOM4.1(予定)では利用可能です。)

[JavaScript]

var attrNode = document.createAttribute("attr1");
attrNode.value = "newValue";
element.setAttributeNode(attrNode);

setAttributeNodeで指定するAttrのインスタンスは、その要素が所属するDocumentで作成されたものでなければなりません。(上記のelementは、documentの子ノードになっているか、そのdocumentのcreateElementで作成されたものである必要があります。)

一方、attributesを使って以下のように書くこともできます。

[JavaScript]

var attrNode = document.createAttribute("attr1");
attrNode.value = "newValue";
element.attributes.setNamedItem(attrNode);

なお、DOM3までは以下のようにTextノードを作成して子ノードして追加する方法もありました。

[JavaScript]

var attrNode = document.createAttribute("attr1");
var textNode = document.createTextNode("newValue");
attrNode.appendChild(textNode);
element.setAttributeNode(attrNode);

※ この方法はDOM4以降では使用できないためブラウザーで挙動が異なり、実行できないブラウザもあります。

属性の削除

属性の値をクリアする場合、空の文字列("")を設定してもクリアされていない場合があります。その場合、ElementremoveAttributeremoveAttributeNode(DOM4を除く)を使います。なお、属性の値を既定値に戻したい場合もこの方法を取ってください。

[JavaScript]

element.removeAttribute("attr1");
[JavaScript]

var attrNode = element.getAttributeNode("attr1");
element.removeAttributeNode(attrNode);

removeAttributeNodeの引数として指定したAttrインスタンスは直ちに無効にならないため、再び同じ要素や別の要素のsetAttributeNodeメソッドに渡すことができます。

スポンサーリンク

名前空間付き属性の利用

XMLにおいて属性を利用する際、既定の名前空間とは異なる特定のXML名前空間に定義された属性を利用したいケースがあります。属性における名前空間の利用については、要素における名前空間と同様に「名前」「ローカル名」「名前空間」の3種類を区別して用いる必要があり、メソッドとライブラリ(実装)によって微妙に扱いが異なるため注意が必要です。

まず以下の要素と属性について考えます。

[XML]

<someElement xmlns:some="http://www.example.com/ns" some:myAttr="Hoge" otherAttr="Piyo" />

このXMLにおいて、「someElement」には3つの属性が定義されていると考えますが、それらの属性は以下のように定義づけられます。

名前 ローカル名 名前空間
xmlns:some some http://www.w3.org/2000/xmlns/ http://www.example.com/ns
some:myAttr myAttr http://www.example.com/ns Hoge
otherAttr otherAttr (空文字列) Piyo

※ それぞれの値は、それぞれAttrインターフェイスのnamelocalNamenamespaceURI、およびvalueの各プロパティーの値に対応します。

まず、「名前空間」が存在する属性の「名前」については、多くの場合「Prefix」と呼ばれる短縮名が付加されており、「<prefix>:<local-name>」の形式となっています。一方「ローカル名」は「Prefix」を除いた名前になっています。もし「名前空間」が存在しない、もしくはsetAttributeNSで「名前」に「Prefix」が指定されていない(およびsetAttributeNodeNSで指定した属性ノードのprefixが無い)場合は、「名前」と「ローカル名」は一致するようになります。

※ 「名前空間」が存在しながら「名前」と「ローカル名」が一致するケースは存在します。これは「既定の名前空間」が利用されている場合の他、明示的にsetAttributeNSなどで「名前空間」を指定しつつ「Prefix」のない「名前」を指定して属性を設定した場合に発生します。
※ 「xml」および「xmlns」のという単語の「Prefix」は予約されており、別の「名前空間」の「Prefix」として用いようとするとエラーになります。

属性を取得する場合、もし「Prefix」込みの「名前」に一致する属性名が分かっている場合は、getAttributeメソッドやgetAttributeNodeメソッドの引数にその「名前」を指定することで属性のデータを取得することができます。しかし「名前空間」を簡略化した「Prefix」はXMLドキュメントによっては一意ではないため、「名前空間」のみを知っている場合は、代わりにgetAttributeNSもしくはgetAttributeNodeNSメソッドを利用します。

[JavaScript]

// (「element」を上記の「someElement」を指す要素オブジェクトとする)

// Prefixが分かっている場合: 名前(=「prefix:localName」)を指定する
var attrVal1 = element.getAttribute("some:myAttr");
// 名前空間が分かっている場合: 名前空間とローカル名をそれぞれ指定する
var attrVal2 = element.getAttributeNS("http://www.example.com/ns", "myAttr");
// attrVal1 === attrVal2

※ 「既定の名前空間」に対応する属性値は単に「Prefix」無しでgetAttribute/getAttributeNodeメソッドを用いるか、既定の名前空間として用いられている「名前空間」の値を用いてgetAttributeNS/getAttributeNodeNSメソッドを利用します。

一方属性の値をセットする場合は、既存の属性値を上書きする場合の名前指定は上記の取得時と同様ですが、「名前」に一致する属性が存在せず新しく属性として追加される場合は使用するメソッドによって以下の通り「名前」「ローカル名」「名前空間」の扱いが異なります。

※ 以下、「修飾名」として扱うとある場合は、「修飾名」はそのまま「名前」となり、さらに「:」(コロン)があればその文字で分割して前者を「Prefix」、後者を「ローカル名」として扱います。もし「:」が無ければそのまま「修飾名」を「ローカル名」(「名前」と同じ、「Prefix」無し)として扱います。

setAttributeの場合
1つ目の引数に指定された文字列は「名前」として、かつ「ローカル名」として扱われます。このとき「名前空間」および「Prefix」は「無し」となります(「修飾名」としては扱われません)。
setAttributeNSの場合
1つ目の引数に指定された文字列が「名前空間」として扱われます。2つ目の文字列は「修飾名」として扱われます。
setAttributeNodeの場合
指定された属性ノードと「名前」が一致する属性がある場合、それを置き換えます(名前空間のチェックは行われません)。無い場合はそのまま追加されます。(いずれの場合も属性ノードの情報が用いられます。)
setAttributeNodeNSの場合
指定された属性ノードと「名前空間」および「ローカル名」が一致する属性がある場合、それを置き換えます(Prefixのチェックは行われません)。無い場合はそのまま追加されます。(いずれの場合も属性ノードの情報が用いられます。)
.NETのSystem.Xml.XmlElement.SetAttribute(String, String)の場合
名前空間指定が無い場合、第1引数はDOM4と異なり「修飾名」として扱われます。「名前空間」は常に「無し」となります。
.NETのSystem.Xml.XmlElement.SetAttribute(String, String, String)の場合
第1引数は「ローカル名」、第2引数は「名前空間」として扱われます。「Prefix」は無しとして扱います。

※ .NETで「ローカル名」「Prefix」「名前空間」を同時に指定したい場合は、XmlDocument.CreateAttributeとXmlElement.SetAttributeNodeを組み合わせて利用する必要があります。

なお、attributes.getNamedItem に指定する引数については、DOM3までは「名前」、DOM4.1では「修飾名」となります(後者の実装はブラウザーによって対応されていない可能性があります)。

最終更新日: 2018/02/20