これまで解説してきた属性プロパティや、getAttribute/setAttributeメソッドによるタグ属性へのアクセス方法は、事前に取得したい属性やセットしたい属性が決まっているときに限って活用できます。しかし場合によっては、どんな属性がセットされているのかをJavaScriptから調べたいというシーンに直面することがあります。
例えば、HTMLがCGIなどのWebアプリケーションによって動的に生成される場合が想定されます。条件に応じてHTMLタグが変化するわけですから、JavaScript側ではその都度、どんなタグにどんな属性がセットされているのかを確認する必要に迫られるのです。このような状況下で、考えうる属性名をやみくもに指定して存在を調べるというのはナンセンスです。要素を指定したら一発で、すべての属性を抜き出せる方法が必要となりますが、それを実現するのがattributesプロパティです。
attributes.html
<img src="normal.gif" border="0" alt="次へ" id="img" />
<script type="text/javascript">
/*ボタンの要素オブジェクト*/
var img = document.getElementById('img');
/*属性を取り出す*/
var attrs = img.attributes;
/*すべての属性名と属性値をアラートウィンドウに表示する*/
var msg = '';
for(var i=0; i<attrs.length; i++) {
/*属性ノードオブジェクト*/
var attr = attrs.item(i);
/*属性名と属性値をメッセージに追加*/
msg = msg + attr.nodeName + ":" + attr.nodeValue + "\n";
}
alert(msg);
</script>
このサンプルは、IMGタグの属性をすべて調べてアラートウィンドウに表示します
サンプルのHTMLをブラウザでアクセスすると、図の通りのアラートウィンドウが表示されます。IMGタグに記述された属性が表示されていることがわかります。図ではFirefox 1.5を使っていますが、Opera 9およびSafari 2.0でも同様の結果が得られます(Opera 9の場合は、属性名が大文字に変換されます)。ただし、順番は同じでないことに注意してください。また、Internet Explorerに関しては、結果が大きく異なります。Internet Explorerではタグに明示的に記述されている属性だけではなく、ブラウザ内部で保持している属性すべてを抜き出してしまいます。この点に関して、Internet Explorerは互換性がないので、注意が必要です。
サンプルでは、attributesプロパティを使って得られた結果を変数attrsに格納しましたが、これはJavaScriptの配列(Arrayオブジェクト)ではありません。DOMで規定されているNamedNodeMapと呼ばれる形式のオブジェクトで、仕様にのっとった方法でしかアクセスしてはいけません。前述のNodeListと同様に、規定されている以外の方法でアクセスすると、期待通りの結果が得られませんので、注意してください。また、attributesプロパティは、要素ノードに対してのみ適用できます。つまり、一般的なHTMLタグ要素であれば利用可能です。テキストノードに対して適用するとnullを返します。
NamedNodeMapには、属性名と属性値のセットがまとめて格納されており、属性名を指定することで属性値を得たり、配列のようにインデックス番号を指定することで属性情報を得ることができます。わかりやすく言うと、配列とハッシュの両方を組み合わせたものです。
サンプルではNamedNodeMap形式のオブジェクトを変数attrsに格納し、for文を使ってそこからすべての要素を抜き出しています。attrsは配列ではありませんので、for/in文を使わないよう注意してください。for文では、lengthプロパティから得られた要素数を、繰り返し処理の最終インデックスとして使います。for文の中では、itemメソッドを使ってインデックス番号を指定し、nodeNameプロパティから属性名を、nodeValueプロパティから属性値を抜き出しています。NamedNodeMap型オブジェクトで使えるプロパティとメソッドは以下の通りです。
| プロパティ | |
|---|---|
| length 要素数を返します。 | |
| メソッド | |
|---|---|
| getNamedItem(name) 属性名を指定すると、その属性ノードオブジェクトを返します。もし指定した属性名が存在しなければnullを返します。 |
|
| item(index) インデックス番号を指定すると、該当の属性ノードオブジェクトを返します。インデックス番号は0から数えます。ただし、順番は保証されません。タグに記述した順番にインデックス番号が割り振られているわけではないので、注意してください。存在しないインデックス番号を指定するとnullを返します。 |
|
| removeNamedItem(name) 指定した属性名に対応する属性を削除します。 |
|
| setNamedItem(arg) 属性ノードオブジェクトを与えることで新たに属性をセットします。もしすでに同じ属性名が存在していれば、上書きします。新たにセットされた場合はnullを返し、上書きした場合は古い属性情報を持った属性ノードオブジェクトを返します。 |
|
| NamedNodeMapのプロパティとメソッド | |
NamedNodeMapのメソッドを使ったタグの属性値の読み書きを見ていきましょう。このサンプルで、getNamedItem/setNamedItemメソッドの用法を確認してください。
attributes_NamedNodeMap.html
<style type="text/css">
#box { width: 150px; height:80px; border: 1px solid #4c4c4c; padding:
12px; text-align: center; }
.red { background-color: #ff0000; }
.gray { background-color: #cccccc; }
</style>
<div id="box" class="gray">
<div>ボタンを押すと、ボックスの背景色が変わります。</div>
<div><input type="button" name="btn" value="背景色変更"
onclick="classChange()" /></div>
</div>
<script type="text/javascript">
function classChange() {
/*DIVタグの要素ノードオブジェクト*/
var box = document.getElementById('box');
/*属性を取り出す*/
var attrs = box.attributes;
/*class属性の属性ノードオブジェクトを取り出す*/
var class_attr_node = attrs.getNamedItem('class');
/*class属性値をアラートウィンドウに表示*/
alert(class_attr_node.nodeValue);
/*(1)新たに属性ノードオブジェクトを生成する*/
var attr_node = document.createAttribute('class')
/*(2)class属性名をセット*/
box.attributes.setNamedItem(attr_node);
/*(3)class属性値を変更*/
attr_node.nodeValue = 'red';
}
</script>
このサンプルは、前節で使ったサンプルをNamedNodeMapのメソッド(getNamedItem/setNamedItem)に置き換えて作り直したものです。ボタンを押すと、現時点でのclass属性の値がアラートウィンドウに表示され、その後、class属性値を書き換えます。class属性の値が書き換わったことで、適用されるスタイルが変わり、背景色がグレーから赤色に変わります。
このサンプルで注目すべき点は、まずgetNamedItemメソッドにより属性名を指定することで属性値を取り出せる点です。先ほどのサンプルでは、NamedNodeMapオブジェクトのitemメソッドによりインデックス番号を使って属性値を取り出す方法を見てきましたが、今度は、属性名を指定して属性値を取り出しています。NamedNodeMapオブジェクトが配列のような振る舞いを持ちながら、ハッシュとしての機能を持ち合わせている点を学んでください。
次に注目すべき点は、属性をセットする手順です。まず(1)で、createAttributeメソッドを使ってclass属性を新たに生成します。createAttributeは初めて紹介するメソッドですが、引数に属性名を指定することで属性ノードオブジェクトを作成します。この段階ではまだ宙に浮いたオブジェクトのため、画面上はなにも変化はありません。仮想的に属性が作られたにすぎない状態です。
(2)で、作成した属性ノードオブジェクトをsetNamedItemメソッドに与え、box.attributesに追加します。これで属性値のない仮想的なclass属性が、DIVタグにセットされた状態となります。この段階でもなにも変化はありません。最後に(3)で、先ほどsetNamedItemメソッドに与えた属性ノードオブジェクトに、nodeValueプロパティの値をセットすると、属性値がセットされたことになり、画面上に反映されます。
setNamedItemメソッドで、指定要素に属性をすでにセットしてしまったのにもかかわらず、あとでその値を自由に操作できるということに違和感を覚えるかもしれません。もちろん、事前にnodeValueを属性ノードオブジェクトにセットしたうえでsetNamedItemメソッドにセットしてもかまいませんが、このサンプルではあえて逆にしています。実は、あとから変更できるという点が、この手法の特徴だからです。属性の情報がオブジェクトとして分離しているため、このようなことが可能になるのです。状況によっては、このような手法が役に立つことがあるでしょう。
Copyright © ITmedia, Inc. All Rights Reserved.