連載
» 2011年10月07日 00時00分 公開

連載:人気順に説明する初めてのHTML5開発:DOM操作の主流になるか!? セレクタAPIを使いこなそう

DOMで要素を取得するのに、煩雑で長い記述はもう不要。シンプルなCSSセレクタの構文を使って簡単に取得できる。

[ナオキ(監修:山田祥寛),WINGSプロジェクト(http://www.wings.msn.to/)]
連載:人気順に説明する初めてのHTML5開発
業務アプリInsider/Insider.NET

powered by Insider.NET

「連載:人気順に説明する初めてのHTML5開発」のインデックス

連載目次

 従来、JavaScriptでは、以下のようなメソッドを利用して、操作対象の要素を特定していた。

  • getElementByIdメソッド:IDの値をキーにノードを取得する。
  • getElementsByTagNameメソッド:要素名をキーにノードを取得する。
  • getElementsByClassNameメソッド:class属性の値をキーにノードを取得する。

 しかし、この方法で複雑な構造のHTML文書から要素を取り出す場合、コードが複雑になりやすいという課題もあった。例えば、以下は従来のgetElementXxxxxメソッドを利用した要素の取得例だ。

// 奇数番目の<li>要素を取得する例
var li = document.getElementsByTagName("li");
for (var i = 0; i < li.length; i++) {
  if (i % 2 == 0)
    li[i].style.background = "yellow";
}

セレクタAPIを使用しない場合の要素の取得例

 上記のJavaScriptコードでは、奇数番目の要素を特定するために全てのインデックス番号をif文で判定させている。ほかにも属性値やclass属性によって要素を絞り込むならば、同じような判定式を書いた経験が読者諸氏にもあるだろう。「もっと簡単に特定要素を取得できれば」「重複処理を可能な限り避けたい」と誰もが思ったはずだ。 そこで昨今では、「セレクタ」と呼ばれる記法が活用される場が増えてきている。セレクタとは、HTML文書内部の要素を、階層構造やclass(クラス)や属性、順番などの条件で取得するためのクエリのこと。旧来からCSSでは利用されている記法だ。外部ライブラリにはなるが、jQueryでもセレクタを使用することで、classやIDなどを指定してシンプルに要素にアクセスできる。 そしてHTML5でもいよいよ、標準の仕様としてセレクタAPIが定義された。セレクタAPIは「CSSセレクタにマッチした要素を取得するためのメソッド」だ。これを利用することで、jQueryに頼ることなく、標準的なJavaScriptのみでjQueryのような要素の取得ができるようになる。

 「document.getElementXxxxx」という記述をセレクタAPIへと置き換えていくことで、JavaScriptコードの量自体も削減が見込め、保守性、可読性も向上するというメリットがあるだろう。

 なお、セレクタAPIはLevel 1とLevel 2に分類されているが、本稿では現在の主要ブラウザで実装済みとなるLevel 1について解説する。まずはセレクタAPIの実装状況だが、セレクタAPIは次の表のとおり、現在、リリースされている主要ブラウザではほとんど実装済みだ。

ブラウザ 対応バージョン
Internet Explorer 8以降
Firefox 3.5以降
Chrome 4以降
Safari 3.1以降
Opera 10.0以降
主要ブラウザにおけるセレクタAPI Level 1の実装状況

 セレクタAPIは簡単に利用でき、該当部分のコードがすっきりするので、Web開発者の生産性を高めるAPIとなる。セレクタAPI Level 1には2種類のメソッドのみが存在する。それぞれ以下の機能を保有している。

メソッド 概要
querySelector("CSSセレクタ") CSSセレクタをキーに最初に該当したノードを取得
querySelectorAll("CSSセレクタ") CSSセレクタをキーにマッチした全てのノードをノードリスト(以下、NodeList)形式で取得
セレクタAPIのメソッドと概要

 なお、querySelectorAllメソッドは戻り値としてNodeListを返すが、このとき取得できるNodeListは静的なNodeListとなる。静的とは、メソッドを呼び出した時点での状態が保持されることを、逆に動的とは、メソッドを呼び出した後のDOM(Document Object Model)の構造変化が反映されることをいう。動的なNodeListでは、呼び出し後の操作によって結果が変動する可能性があるので、注意してほしい。

【コラム】セレクタAPI Level 2

 セレクタAPI Level 2は、DOM内でセレクタを評価できるようなAPIをそろえている。現在は開発者向けリリース版でのみ実装が進んでいるが、近い将来、利用できるようになる機能として頭の片隅にでも置いておくとよいだろう。

 現在、予定されているセレクタAPI Level 2のメソッドは3つある。

  • matchesSelectorメソッド:ドキュメントや要素に対してセレクタが一致するかを判定する。
  • queryScopedSelector/queryScopedSelectorAllメソッド:CSSセレクタLevel 4で定義されている:scope疑似クラスを利用して該当するノードやノードリストを取得する。

 「:scope」は、範囲を限定した一致検索を行うための疑似クラスである。


どの範囲に適用させるかを特定するCSSセレクタ

 セレクタAPIをきちんと理解するには、まずCSSセレクタの知識が必要になる。まずは、CSSセレクタについておさらいをしてみよう。以下は、簡単なCSSの例である。

p { font-style: italic; }                     (1)
.highlight { background-color: yellow; }      (2)

CSSルールの基本

 多くのCSSルールは、セレクタとプロパティの適用から成り立つ。(1)は、全ての<p>要素の文字列をイタリック体にし、(2)はclass属性が「highlight」である要素に対して背景の色を黄色にする。以下に、CSS3で使用できる主なセレクタをまとめてみた。

セレクタ セレクタ記述例 概要
ユニバーサル・セレクタ * 全ての要素にスタイルを適用する
タイプ・セレクタ a
p
div
(上記のようなHTMLタグの名前)
記述した要素にスタイルを適用する
IDセレクタ #ID名 記述したIDと一致する要素にスタイルを適用する
クラス・セレクタ .クラス値 記述したクラスと一致する要素にスタイルを適用する
グループ・セレクタ h1, h2, h3 コンマを使用して複数の要素にスタイルを一括適用する
子セレクタ div > p
ul > li
「>」を使用して親要素の直下にある子要素に対してスタイルを適用する
疑似クラス・セレクタ :visited
:hover
要素:nth-child
記述した疑似クラスの要素にスタイルを一括適用する
属性セレクタ input[type="email"]
div[margin]
該当の属性または、属性と属性値が一致する要素に対してスタイルを適用する
CSSセレクタの種類と概要

 注目していただきたいのは、最後の属性セレクタだ。属性セレクタは、要素で示された属性について絞り込みを行う。ほかにも属性セレクタには前方一致、部分一致、後方一致による指定もできる。基本的な構文は以下のとおりだ。

セレクタ 取得する要素
要素[属性] 属性が存在する要素
要素[属性='属性値'] 属性と属性値が一致する要素
要素[属性^='val'] 属性値が「val」(=任意の文字列)から始まる要素
要素[属性$='val'] 属性値が「val」で終わる要素
要素[属性*='val'] 属性値が「val」を含む要素
属性セレクタの構文例

 属性セレクタはIDやclass(クラス)以外にも、任意の属性で要素を抽出できるのが特徴だ。それでは、実際にCSSセレクタを使用してセレクタAPIを使用する例を紹介する。

querySelector/querySelectorAllメソッド:セレクタで目的の要素を取り出す

 ここからは、querySelector/querySelectorAllメソッドを利用した、具体的なサンプルを見ていく。サンプルはここから試せるので、セレクタAPIが実装されているブラウザ上で動作を体感していただきたい。 サンプルを実行すると、箇条書きリストと画像が配置された枠が表示される。リスト内の項目をクリックすることで、それぞれの要素が変化する。実行直後の画面は図1のとおり。

図1 サンプル実行直後の画面

 実際の処理の内容は以下のとおりだ。

……省略……
<p>選択した項目の処理の実行か、選択したアルバムのみ表示させます。</p>
<ul>
  <li><a href="#" onclick="selectLi()" >3つ目のLI要素の背景を赤に変更</a></li>
  <li><a href="#" onclick="selectLiOdd()" >奇数のLI要素の背景を黄に変更</a></li>
  <li><a href="#" onclick="selectPadding()" >Padding指定</a></li>
  <li><a href="#" onclick="selctContents('animal')" >動物アルバム</a></li>
  <li><a href="#" onclick="selctContents('meal')" >主食アルバム</a></li>
  <li><a href="#" onclick="selctContents('sweets')" >お菓子アルバム</a></li>
</ul>
<hr />

<!-- (1)<span>要素と<img>要素における簡易アルバム -->
<h3 class="animal">動物アルバム</h3>
<span class="animal" style="width:700px; height:210px; display:block; background-color:green; padding:10px;">
  <img src="cat1.jpg" />
</span>

<h3 class="meal">ご飯アルバム</h3>
<span class="meal" style="width:700px; height:300px; display:block; background-color:blue; padding:10px;">
  <img src="三色丼.jpg" />
  <img src="ハンバーガー.jpg" />
</span>

<h3 class="sweets">お菓子アルバム</h3>
<span class="sweets" style="width:700px; height:300px; display:block; background-color:yellow; padding:10px;">
  <img src="チーズケーキ.jpg" />
  <img src="ホットケーキ.jpg" />
</span>

<h3 class="animal">動物アルバム2</h3>
<span class="animal" style="width:700px; height:210px; display:block; background-color:red; padding:10px;">
  <img src="熊.jpg" />
</span>

<script type="text/javascript">
// (2)条件にあてはまる<li>要素に背景色を指定
function selectLi() {
  // 疑似クラスで、3つ目の<li>要素を取得
  var li = document.querySelector('li:nth-child(3)');
  li.style.background = "red";
}

// 条件にあてはまる<li>要素に背景色を指定
function selectLiOdd() {
  // 疑似クラスで、奇数の<li>要素を取得
  var li = document.querySelectorAll('li:nth-child(odd)');
  for (var i = 0; i < li.length; i++) {
    li[i].style.background = "yellow";
  }
}

// (3)<span>要素直下の<img>要素にパディングを指定
function selectPadding() {
  // <span>要素直下の<img>要素を全て取得
  var img = document.querySelectorAll('span > img');
  for (var i = 0; i < img.length; i++) {
    img[i].style.padding = "15px";
  }
}

// (4)ページ上のコンテンツの表示/非表示を制御
function selctContents(clsName) {
  // <h3>要素と<span>要素を全て取得
  var hide = document.querySelectorAll('h3, span');
  for (var i = 0; i < hide.length; i++) {
    hide[i].style.display = "none";
  }

  // 関数引数に指定されたクラス名の要素を全て取得
  var disp = document.querySelectorAll("." + clsName);
  for (var i = 0; i < disp.length; i++) {
    disp[i].style.display = "block";
  }
}
</script>
……省略……

セレクタAPIを使用したサンプル(selector_api.htm

 それではコードの説明をしよう。

(1)<span>要素と<img>要素における簡易アルバム

 <span>要素と<img>要素には、それぞれclass名にアルバム名として分かりやすいものを設定している。今回は設定した値をセレクタとして利用する。

(2)条件にあてはまる<li>要素に背景色を指定

 こちらは、querySelectorメソッドとquerySelectorAllメソッドを用いて要素を取得している。

 querySelectorメソッドは、最初に該当した要素のみを取得する。「li:nth-child(3)」というCSSセレクタは、全ての<li>要素のうち、その親要素(=上記のコード例では<ul>要素)から見て3番目の子要素となる<li>要素(=今回の例では「Padding指定」というリスト項目)を取得し、その後のコードによりその要素の背景色を設定している。

 querySelectorAllメソッドは、該当する要素全てをNodeListで取得する。CSSセレクタは上記と同様の記載をしているが、条件式の部分には奇数を指す「odd」を用いて奇数番目の<li>要素を取得し、ループ内で、NodeListの各要素に対して順に背景色を設定している。 図2は最初のリスト項目をクリックした際の挙動で、3つ目の項目の背景色が赤色に変更されている。

図2 1つ目のリスト項目をクリックしたときの画面(querySelectorメソッド使用時の挙動)

 図3は2つ目のリスト項目をクリックした際の挙動で、奇数番目のリスト項目の背景色が黄色に変更されている。

図3 2つ目のリスト項目をクリックしたときの画面(querySelectorAllメソッド使用時の挙動)

(3)<span>要素直下の<img>要素にパディングを指定

 基本的な記述は(2)と同様だ。「span > img」というCSSセレクタにより、子セレクタを用いて<span>要素直下の<img>要素を指定している。図4は3つ目のリスト項目をクリックした際の挙動で、画像に対してパディング(=余白)を追加している。

図4 3つ目のリスト項目をクリックしたときの画面(querySelectorAllメソッド使用時の挙動)

(4)ページ上のコンテンツの表示/非表示を制御

 基本的な記述方法は(2)や(3)と同様だ。関数引数としてアルバムを判断するためのクラス名を用意している。先に、「h3, span」というグループ・セレクタにより、全ての<h3>要素と<span>要素を取得している。その後、取得した各要素を非表示に設定している。続いて「.クラス名」というCSSセレクタにより、関数引数として渡されたクラス名を持つ要素を全て取得している。その後、各要素を表示に設定している。 図5は4つ目のリスト項目をクリックした際の挙動で、全てのアルバム領域の中で、動物アルバムのみを表示するように変更している。

図5 4つ目のリスト項目をクリックしたときの画面(querySelectorAllメソッド使用時の挙動)

 図6は5つ目のリスト項目をクリックした際の挙動で、全てのアルバム領域の中で、主食アルバムのみの表示に変更している。

図6 5つ目のリスト項目をクリックしたときの画面(querySelectorAllメソッド使用時の挙動)

 図5は4つ目のリスト項目をクリックした際の挙動で、全てのアルバム領域の中で、お菓子アルバムのみの表示に変更している。

図7 6つ目のリスト項目を選択したときの画面(querySelectorAllメソッド使用時の挙動)

 以上のコード例から、要素の取得が容易にできていることが確認できたのではないだろうか。

■まとめ

 今回は、DOM操作の主流になるであろうセレクタAPIについて紹介した。jQueryを使える場合は活用の場が少ないかもしれないが、複数のgetElementXxxxxメソッドを使用しているような状況では、表現力に優れ、シンプルに要素を取得できるセレクタAPIは十分に有用であろう。現存するほぼ全てのブラウザでLevel 1の実装はされているので、積極的に組み込んでいくことをおススメしたい。

 次回は、リアルタイムWebを実現するためのWebSocketを紹介する。お楽しみに。

「連載:人気順に説明する初めてのHTML5開発」のインデックス

連載:人気順に説明する初めてのHTML5開発

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。