だれも聞いていないと思って歌え

dance as if no one’s watching, sing as if no one’s listening, and live everyday as if it were your last.

document.querySelector と document.querySelectorAll について

仕事でよく使っているけど、ちゃんと理解できていなかったので改めて調べてみました。

document.querySelector と document.querySelectorAll

document.querySelectordocument.querySelectorAll とは W3C によって定義された Selectors API の仕様です。 jQuery のようにセレクターを指定して、要素を取得できるメソッドです。

  • document.querySelector … 指定された CSS セレクタにマッチする文書中の最初の要素を返す。
  • document.querySelectorAll … 指定された CSS セレクタにマッチする文書中の要素の全てのリストを返す。

また、同様に文書中の要素を取得するメソッドとして下記があります。

  • document.getElementById … 指定されたIDを持つ要素を返す。
  • document.getElementsByClassName … 指定されたクラス名を持つ要素のリストを返す。

下記のHTML文書を例に、これらの違いについてまとめます。

<div class="news-box">
  <h2>NEWS</h2>
  <ul>
    <li class="news">news title 1</li>
    <li>topic title 2</li>
    <li class="news">news title 3</li>
  </ul>
</div>
<div class="articles-box">
  <h2>ARTICLES</h2>
  <ul>
    <li class="article">article list 1</li>
    <li>article list 2</li>
  </ul>
</div>

要素を一件だけ取得する

NEWSのnewsクラスではないリストの先頭の取得する場合、以下は同じ結果を取得することができます。 document.querySelector を使わない場合、一行で書く方法が分かりませんでした・・・

const elements = document.getElementsByClassName('news-box')[0].getElementsByTagName('li');
let elm = [];
for(var i = 0; i < elements.length; i++) {
  if(elements[i].className !== 'news') {
    elm.push(elements[i]);
  }
}
const element = elm[0];

> <li class="news">news title 1</li>
document.querySelector('.news-box li:not(.news)');

> <li class="news">news title 1</li>

ただし、単発のIDセレクタを指定する場合は document.getElementById の方が高速になります。

要素のリストを取得する

NEWSのnewsクラスのリストを取得する場合、以下は同じ結果を取得することができます。

document.getElementsByClassName('news-box')[0].getElementsByClassName('news');

> [<li class=​"news">​news title 1​</li>​, <li class=​"news">​news title 3​</li>​]
document.querySelectorAll('.news-box .news');

> [<li class=​"news">​news title 1​</li>​, <li class=​"news">​news title 3​</li>​]

こちらも、単発のクラス名を指定する場合は document.getElementsByClassName の方が高速になります。

引数に配列を指定する

ちなみに、引数には配列が指定可能で、 NEWSのnewsクラスのリスト と ARTICLESのarticleクラスのリスト のみを取得することができます。

document.querySelectorAll(['.news-box li.news', '.articles-box li:article']);

> [<li class=​"news">​news title 1​</li>​, <li class=​"news">​news title 3​</li>​, <li class=​"article">​article list 1​</li>​]

CSS セレクタ

これを知っておくと便利だなと思ったものについてまとめました。

:not()

上記例で使っていますが、引数で指定したセレクターの要素を除外します。

document.querySelectorAll('.news-box li:not(.news)');

> [<li>​topic title 2​</li>​]

:first-child, :last-child

:first-child では最初の子要素を取得します。

document.querySelectorAll('.news-box li:first-child');

> [<li class=​"news">​news title 1​</li>​]

ただし、 document.querySelectorAll('.news-box li:not(.news):first-child')擬似クラスを複数組み合わせて使用することはできません(後述します。)

:last-child は最後の子要素を取得できます。

document.querySelectorAll('.news-box li:last-child');

> [<li class=​"news">​news title 3​</li>​]

:nth-child(), :nth-last-child()

引数には子要素のn番目、偶数(even)、奇数(odd)などが指定可能です。

document.querySelectorAll('.news-box li:nth-child(odd)');

> [<li class=​"news">​news title 1​</li>​, <li class=​"news">​news title 3​</li>​]

:nth-last-child() も同様に、最後の子要素から逆向きに数えてn番目、偶数(even)、奇数(odd)と指定できます。

擬似クラスを複数組み合わせる

上にあげた例と同じになりますが、NEWSのnewsクラスを除外したリストの先頭を取得しようと以下のように記述します。

document.querySelectorAll('.news-box li:not(.news):first-child');

> []

しかし、取得対象の :first-child が除外対象の .news であるため空の配列が返ってきます。 :nth-child() を使うか document.querySelector で先頭の一件のみ取得する必要があります。

また、下記のように :not() を複数繋げて使用することも可能です。

document.querySelectorAll('li:not(.news):not(.article)');

> [<li>​topic title 2​</li>​, <li>​article list 2</li>​]

JavaScript勉強中です。 間違いありましたら指摘お願いします。

参考