0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptでDOM操作を学び始めると、かなり早い段階で次のようなコードに出会います。

const buttons = document.querySelectorAll(".accordion-button");

buttons.forEach(function (button) {
  console.log(button);
});

このコードを見たとき、こう思いやすいです。

querySelectorAll()で取得したものって、配列なの?」

結論から言うと、querySelectorAll()で取得できるものは、ArrayではなくNodeListです。

ただし、見た目はかなり配列に似ています。

この記事では、実際のHTMLを使いながら、ArrayNodeListの違いを整理します。

まずHTMLを用意する

今回は、次のようなアコーディオン風のHTMLを例にします。

<div class="accordion">
  <div class="accordion-item">
    <div class="accordion-header js-accordion-button">
      <h2 class="accordion-title">HTMLとは?</h2>
      <span class="accordion-icon">+</span>
    </div>
    <div class="accordion-content">
      <p>HTMLは、Webページの構造を作るための言語です。</p>
    </div>
  </div>

  <div class="accordion-item">
    <div class="accordion-header js-accordion-button">
      <h2 class="accordion-title">CSSとは?</h2>
      <span class="accordion-icon">+</span>
    </div>
    <div class="accordion-content">
      <p>CSSは、Webページの見た目を整えるための言語です。</p>
    </div>
  </div>

  <div class="accordion-item">
    <div class="accordion-header js-accordion-button">
      <h2 class="accordion-title">JavaScriptとは?</h2>
      <span class="accordion-icon">+</span>
    </div>
    <div class="accordion-content">
      <p>JavaScriptは、Webページに動きをつけるための言語です。</p>
    </div>
  </div>
</div>

ここでは、.js-accordion-buttonというクラスが付いた要素が3つあります。

つまり、次の3つです。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">HTMLとは?</h2>
  <span class="accordion-icon">+</span>
</div>
<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">CSSとは?</h2>
  <span class="accordion-icon">+</span>
</div>
<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">JavaScriptとは?</h2>
  <span class="accordion-icon">+</span>
</div>

この3つをJavaScriptで取得してみます。

const buttons = document.querySelectorAll(".js-accordion-button");

console.log(buttons);

ブラウザのコンソールには、だいたい次のように表示されます。

NodeList(3) [
  div.accordion-header.js-accordion-button,
  div.accordion-header.js-accordion-button,
  div.accordion-header.js-accordion-button
]

ここで重要なのは、表示がArrayではなくNodeListになっていることです。

NodeListとは何か

NodeListとは、DOMから取得した複数のノードの集まりです。

今回で言えば、.js-accordion-buttonに一致した3つのdiv要素の集まりです。

const buttons = document.querySelectorAll(".js-accordion-button");

このbuttonsの中には、次のような要素が順番に入っています。

buttons[0] // HTMLとは?のボタン
buttons[1] // CSSとは?のボタン
buttons[2] // JavaScriptとは?のボタン

実際に確認できます。

console.log(buttons[0]);
console.log(buttons[1]);
console.log(buttons[2]);

出力されるのは、それぞれのHTML要素です。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">HTMLとは?</h2>
  <span class="accordion-icon">+</span>
</div>
<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">CSSとは?</h2>
  <span class="accordion-icon">+</span>
</div>
<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">JavaScriptとは?</h2>
  <span class="accordion-icon">+</span>
</div>

このように、NodeListは複数のHTML要素を順番に持っています。

だから配列のように見えます。

しかし、配列そのものではありません。

Arrayとは何か

Arrayは、JavaScriptの配列です。

const numbers = [100, 200, 300];

このnumbersArrayです。

console.log(numbers);

出力は次のようになります。

[100, 200, 300]

配列なので、map()filter()などの配列用メソッドを使えます。

const numbers = [100, 200, 300];

const doubledNumbers = numbers.map(function (number) {
  return number * 2;
});

console.log(doubledNumbers);

出力は次のようになります。

[200, 400, 600]

Arrayは、JavaScriptがもともと持っている「配列」です。

一方、NodeListはDOM操作で出てくる「ノードのリスト」です。

ArrayとNodeListの違い

違いを一言で言うと、こうです。

ArrayはJavaScriptの配列です。

NodeListはDOMから取得した要素の集まりです。

見た目は似ていますが、正体が違います。

const numbers = [100, 200, 300];
console.log(numbers);

これはArrayです。

const buttons = document.querySelectorAll(".js-accordion-button");
console.log(buttons);

これはNodeListです。

どちらも次のように番号でアクセスできます。

numbers[0];
buttons[0];

どちらもlengthを使えます。

console.log(numbers.length);
console.log(buttons.length);

しかし、使えるメソッドが違います。

NodeListは配列ではないのでmapがそのまま使えない

たとえば、配列ならmap()が使えます。

const numbers = [100, 200, 300];

const result = numbers.map(function (number) {
  return number * 2;
});

console.log(result);

これは問題なく動きます。

しかし、NodeListに対して同じ感覚でmap()を使うとエラーになります。

const buttons = document.querySelectorAll(".js-accordion-button");

const titles = buttons.map(function (button) {
  return button.textContent;
});

console.log(titles);

これは動きません。

理由は、buttonsArrayではなくNodeListだからです。

NodeListには、map()が用意されていません。

では、なぜNodeListでforEachは使えるのか

ここが初心者にとって一番混乱しやすいところです。

NodeListは配列ではありません。

しかし、最近のブラウザではNodeListにもforEach()があります。

そのため、次のコードは動きます。

const buttons = document.querySelectorAll(".js-accordion-button");

buttons.forEach(function (button) {
  console.log(button);
});

ここでやっていることは、.js-accordion-buttonが付いた要素を1つずつ取り出しているだけです。

1回目のbuttonには、HTMLのボタンが入ります。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">HTMLとは?</h2>
  <span class="accordion-icon">+</span>
</div>

2回目のbuttonには、CSSのボタンが入ります。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">CSSとは?</h2>
  <span class="accordion-icon">+</span>
</div>

3回目のbuttonには、JavaScriptのボタンが入ります。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">JavaScriptとは?</h2>
  <span class="accordion-icon">+</span>
</div>

つまり、次のような処理をしているイメージです。

console.log(buttons[0]);
console.log(buttons[1]);
console.log(buttons[2]);

これをまとめて書けるのがforEach()です。

forEachでクリックイベントを付ける

実際のDOM操作では、取得した要素に対してクリックイベントを付けることが多いです。

const buttons = document.querySelectorAll(".js-accordion-button");

buttons.forEach(function (button) {
  button.addEventListener("click", function () {
    console.log("クリックされました");
  });
});

このコードは、3つの.js-accordion-buttonすべてにクリックイベントを付けています。

つまり、実際には次のようなことをしています。

buttons[0].addEventListener("click", function () {
  console.log("クリックされました");
});

buttons[1].addEventListener("click", function () {
  console.log("クリックされました");
});

buttons[2].addEventListener("click", function () {
  console.log("クリックされました");
});

ただし、毎回このように書くのは面倒です。

だからforEach()を使って、まとめて処理します。

NodeListをArrayに変換する

NodeListに対してmap()filter()を使いたい場合は、Arrayに変換します。

代表的な方法はArray.from()です。

const buttons = document.querySelectorAll(".js-accordion-button");

const buttonArray = Array.from(buttons);

console.log(buttonArray);

これでNodeListArrayに変換できます。

変換後はmap()が使えます。

const buttons = document.querySelectorAll(".js-accordion-button");

const buttonArray = Array.from(buttons);

const titles = buttonArray.map(function (button) {
  return button.textContent;
});

console.log(titles);

出力は次のようになります。

[
  "HTMLとは?+",
  "CSSとは?+",
  "JavaScriptとは?+"
]

textContentは、その要素の中にある文字を取得します。

今回のbuttonの中にはh2spanがあるので、タイトルと+の文字が取得されています。

スプレッド構文でもArrayに変換できる

Array.from()以外に、スプレッド構文でも変換できます。

const buttons = document.querySelectorAll(".js-accordion-button");

const buttonArray = [...buttons];

console.log(buttonArray);

これでもNodeListArrayにできます。

その後は、配列としてmap()を使えます。

const titles = [...buttons].map(function (button) {
  return button.textContent;
});

console.log(titles);

タイトルだけを取得したい場合

先ほどの例では、textContent+まで取得されてしまいました。

タイトルだけを取得したい場合は、各ボタンの中にある.accordion-titleを取得します。

const buttons = document.querySelectorAll(".js-accordion-button");

const titles = [...buttons].map(function (button) {
  const title = button.querySelector(".accordion-title");
  return title.textContent;
});

console.log(titles);

出力は次のようになります。

[
  "HTMLとは?",
  "CSSとは?",
  "JavaScriptとは?"
]

ここでは、まず.js-accordion-buttonを3つ取得しています。

そのあと、各ボタンの中から.accordion-titleを探しています。

const title = button.querySelector(".accordion-title");

つまり、全体から探すのではなく、それぞれのボタンの中からタイトルを探しているということです。

querySelectorAllはNodeListを返す

ここまでの話を整理すると、querySelectorAll()NodeListを返します。

const buttons = document.querySelectorAll(".js-accordion-button");

このbuttonsは配列ではありません。

NodeListです。

だから、次のような理解になります。

const buttons = document.querySelectorAll(".js-accordion-button");

これは、.js-accordion-buttonが付いたHTML要素を全部探して、NodeListとして返している。

buttons.forEach(function (button) {
  console.log(button);
});

これは、NodeListの中の要素を1つずつ取り出して処理している。

const buttonArray = Array.from(buttons);

これは、NodeListArrayに変換している。

querySelectorとの違い

似たものにquerySelector()があります。

const button = document.querySelector(".js-accordion-button");

これは、最初に見つかった1つだけを取得します。

今回のHTMLでは、.js-accordion-buttonが3つあります。

しかし、querySelector()で取得できるのは最初の1つだけです。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">HTMLとは?</h2>
  <span class="accordion-icon">+</span>
</div>

一方、querySelectorAll()は一致するものを全部取得します。

const buttons = document.querySelectorAll(".js-accordion-button");

取得されるのは3つです。

<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">HTMLとは?</h2>
  <span class="accordion-icon">+</span>
</div>
<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">CSSとは?</h2>
  <span class="accordion-icon">+</span>
</div>
<div class="accordion-header js-accordion-button">
  <h2 class="accordion-title">JavaScriptとは?</h2>
  <span class="accordion-icon">+</span>
</div>

つまり、querySelector()は1個、querySelectorAll()は全部です。

そして、querySelectorAll()で返ってくる全部の集まりがNodeListです。

まとめ

ArrayはJavaScriptの配列です。

NodeListはDOMから取得したノードの集まりです。

querySelectorAll()で取得したものは、ArrayではなくNodeListです。

NodeListは番号でアクセスできます。

buttons[0];
buttons[1];
buttons[2];

lengthも使えます。

buttons.length;

forEach()も使えます。

buttons.forEach(function (button) {
  console.log(button);
});

しかし、map()filter()などの配列メソッドをそのまま全部使えるわけではありません。

配列として扱いたい場合は、Array.from()やスプレッド構文でArrayに変換します。

const buttonArray = Array.from(buttons);
const buttonArray = [...buttons];

初心者のうちは、次のように覚えると分かりやすいです。

Arrayは、JavaScriptの普通の配列。

NodeListは、HTMLから取ってきた要素のリスト。

NodeListは配列っぽいけど、配列そのものではない。

これが、ArrayNodeListの一番大事な違いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?