Help us understand the problem. What is going on with this article?

要素の取得方法まとめ

はじめに

JavaScriptで要素を取得する時に使う時 jQuery などの便利系を除いていろいろありますが、それらの関係情報や使い方のメモです。

前知識

要素を取得する前にいろいろ知っておきましょう。
噛み砕いた簡単な説明なので、ツッコミはなしの方向で……

Node

ノードとは、簡単に言うとDOMを構成する文字列、コメント、タグなどを扱うためのインターフェース(様々な処理を行うメソッドが含まれたオブジェクトと考えるといいかも)です。
<!DOCTYPE html><!-- コメント --><span>Text</span> のspan要素も、その中のテキストもひとつのノードです。

Element, HTMLElement, HTML*Element

要素、エレメント、いわゆるHTMLなどのタグを示すインターフェースです。
Node を継承しています。

Element

主に XML で使用される要素です。Node を親に持つインターフェースです。
XML では独自の要素を作れますが、主要なものでは <xml><svg> などが当てはまります。

HTMLElement

汎用的な HTML の要素です。Element を継承したインターフェースです。
HTML5において文章の意味を示すための要素 <header><main><footer><article><section><aside><nav> などが当てはまります。

HTML*Element

HTMLElement を更に継承した独自のインターフェースです。
<div> なら HTMLDivElement<a> なら HTMLAnchorElement<h1><h6> なら HTMLHeadingElement といった具合です。

独自のプロパティやメソッドを持ったものもありますが、HTMLDivElement のように HTMLElement を継承しただけで特に意味がないものもあったりします。

HTMLCollection

現在の状態の要素の集合を表すオブジェクト(インターフェース)で、構造は配列に似ています。

HTML*Collection

HTMLCollection を継承した独自のインターフェースです。

これは例えば、<form> 要素内のフォーム系要素を取得する HTMLFormElement.elementsHTMLFormControlsCollection というものになっています。

NodeList

DOMノードの集合を表すオブジェクト(インターフェース)で、構造は配列に似ています。

HTMLCollectionNodeList の違い

一見するとどちらも要素の配列的なもので同じに思えますが、細かいところが違っています。

HTMLCollection の特徴

HTML要素以外のノードを含まない配列っぽいインターフェースです。

これは現在の状態のDOM内の要素をリスト化しているので、DOMに変更があった場合はリアルタイムで中身が変わります。
静的な配列のように扱いたい場合は注意しないといけません。

NodeList の特徴

NodeList とはその名の通り、ノード全般を含む配列っぽいインターフェースです。
なので、要素以外にもテキストノードやコメントノードももしかしたら含まれているかもしれません。
(要素取得系のメソッドで得られる NodeList は要素のみなのであまり心配いりません)

他にも、Array.prototype.forEach のように NodeList.prototype.forEach が実装されているので、配列のように簡単に処理できます。(IEは非対応)

基本的に配列のように静的なオブジェクトですが、Node.childNodes で取得される NodeListHTMLCollection のようにDOMに変更があった場合は中身が変わる可能性があります。


それでは本題

要素が取得できるメソッド

要素のオブジェクトの型については HTMLElement として統一して扱います。

document.getElementById()

返り値: HTMLElement / null

言わずと知れた、指定した ID を持つ要素を取得します。
もし存在しなかった場合は null が返ります。

万が一、DOMに同じIDを持つ要素が複数存在する場合は最初の要素しか取得しません。

HTML
<span id="spanText">Text</span>
JavaScript
var spanText = document.getElementById('spanText');
console.log(spanText);              // <span>
console.log(spanText.textContent);  // "Text"

var divText = document.getElementById('divText');
console.log(divText);  // null

document.getElementsByTagName(), element.getElementsByTagName()

返り値: HTMLCollection

タグ名を指定して取得します。
HTMLCollection なので、DOMの操作をした場合に中身が変わる可能性があります。

HTML
<ul id="textList">
  <li>Text 1</li>
  <li>Text 2</li>
  <li>Text 3</li>
</ul>
<ul>
  <li id="foo">Text 4</li>
</ul>
JavaScript
/* document 全体から li 要素を取得 */
var li01 = document.getElementsByTagName('li');

console.log(li01);  // HTMLCollection (4) [li, li, li, li#foo]

for (var i = 0; li01.length; i++) {
  console.log(li01[i].textContent);
}
// "Text 1"
// "Text 2"
// "Text 3"
// "Text 4"

/* 特定の要素内から li 要素を取得 */
var
  textList = document.getElementById('textList'),
  li02 = textList.getElementsByTagName('li');

console.log(li02);  // HTMLCollection (3) [li, li, li]

for (var j = 0; li02.length; j++) {
  console.log(li02[j].textContent);
}
// "Text 1"
// "Text 2"
// "Text 3"

document.getElementsByClassName(), element.getElementsByClassName()

返り値: HTMLCollection

クラス名を指定して取得します。
HTMLCollection なので、DOMの操作をした場合に中身が変わる可能性があります。

HTML
<ul id="textList">
  <li>Text 1</li>
  <li class="foo">Text 2</li>
  <li>Text 3</li>
</ul>
<p class="foo">Text 4</p>
JavaScript
/* document 全体から .foo を取得 */
var foo01 = document.getElementsByClassName('foo');

console.log(foo01);  // HTMLCollection (2) [li.foo, p.foo]

for (var i = 0; foo01.length; i++) {
  console.log(foo01[i].textContent);
}
// "Text 2"
// "Text 4"

/* 特定の要素内から .foo を取得 */
var
  textList = document.getElementById('textList'),
  foo02 = textList.getElementsByTagName('li');

console.log(foo02);  // HTMLCollection (1) [li.foo]

for (var i = 0; foo02.length; i++) {
  console.log(foo02[i].textContent);
}
// "Text 2"

document.getElementsByName()

返り値: NodeList

要素の name 属性の値を元に要素を取得します。
このメソッドはブラウザごとに動作が異なる場合があるだけでなく、HTML5においてはほとんど活躍することはないと思います。

HTML
<input type="hidden" name="foo" value="Text 1">
<input type="text" name="foo" value="Text 2">
<select name="foo">
  <option>Text 3</option>
  <option selected>Text 4</option>
  <option>Text 5</option>
</select>
JavaScript
var foo = document.getElementsByName('foo');

console.log(foo);  // NodeList (3) [input, input, select]

foo.forEach(function (elem) {
  console.log(elem.value);
});
// "Text 1"
// "Text 2"
// "Text 4"

document.querySelector(), element.querySelector()

返り値: HTMLElement

CSSセレクタでマッチした最初の要素を取得します。
CSS3のセレクタの記述もブラウザが対応していれば可能です。

HTML
<p class="foo">Text 1</p>
<ul>
  <li>Text 2</li>
  <li class="foo">Text 3</li>
  <li>Text 4</li>
  <li>Text 5</li>
</ul>
<ul id="textList">
  <li class="foo">Text 6</li>
  <li>Text 7</li>
</ul>
JavaScript
/* 最初の .foo を取得 */
var foo01 = document.querySelector('.foo');

console.log(foo01);              // <p>
console.log(foo01.textContent);  // "Text 1"

/* 最初の ul .foo を取得 */
var foo02 = document.querySelector('ul .foo');

console.log(foo02);              // <li>
console.log(foo02.textContent);  // "Text 3"

/* 特定の要素内から最初の .foo を取得 */
var
  textList = document.getElementById('textList'),
  foo03 = textList.querySelector('.foo');

console.log(foo03);              // <li>
console.log(foo03.textContent);  // "Text 6"

/* 最初の li:last-child を取得 */
var lastChild = document.querySelector('li:last-child');

console.log(lastChild);              // <li>
console.log(lastChild.textContent);  // "Text 5"

特定のIDの要素を取得する時

document.getElementById('foo') よりも document.querySelector('#foo') の方が直感的でわかりやすいかもしれませんが、getElementById() の方が高速な処理になります。(といっても人間の感覚ではわからないですが)

document.querySelectorAll(), element.querySelectorAll()

返り値: NodeList

CSSセレクタでマッチした要素を取得して NodeList として返します。
CSS3のセレクタの記述もブラウザが対応していれば可能です。

jQuery の $('selector') に近いメソッドです。

HTML
<p class="foo">Text 1</p>
<ul>
  <li>Text 2</li>
  <li class="foo">Text 3</li>
  <li>Text 4</li>
  <li>Text 5</li>
</ul>
<ul id="textList">
  <li class="foo">Text 6</li>
  <li>Text 7</li>
</ul>
JavaScript
/* .foo を取得 */
var foo01 = document.querySelectorAll('.foo');

console.log(foo01);  // NodeList (3) [p.foo, li.foo, li.foo]

foo01.forEach(function (elem) {
  console.log(elem.textContent);
});
// "Text 1"
// "Text 3"
// "Text 6"

/* ul .foo を取得 */
var foo02 = document.querySelectorAll('ul .foo');

console.log(foo02);  // NodeList (2) [li.foo, li.foo]

foo02.forEach(function (elem) {
  console.log(elem.textContent);
});
// "Text 3"
// "Text 6"

/* 特定の要素内から最初の .foo を取得 */
var
  textList = document.getElementById('textList'),
  foo03 = textList.querySelectorAll('.foo');

console.log(foo03);  // NodeList (1) [li.foo]

foo03.forEach(function (elem) {
  console.log(elem.textContent);
});
// "Text 6"

/* li:last-child を取得 */
var lastChild = document.querySelectorAll('li:last-child');

console.log(lastChild);  // NodeList (2) [li, li]

lastChild.forEach(function (elem) {
  console.log(elem.textContent);
});
// "Text 5"
// "Text 7"

特定のタグまたはクラス名を取得する時

document.getElementsByTagName('div') よりも document.querySelectorAll('div')document.getElementsByClassName('.foo') よりも document.querySelectorAll('.foo') の方が直感的でわかりやすいかもしれませんが、それぞれ getElementsByTagName()getElementsByClassName() の方が高速な処理になります。

取得した NodeListHTMLCollection の使い方

要素を直接取得できる document.getElementById()document.querySelector() と違い、それ以外のメソッドに関しては NodeList または HTMLCollection のオブジェクトであり、そのままでは要素を扱えません。

そこで、配列のようなオブジェクトなので for 文のように反復処理をさせる必要がありますが、Array.prototype.forEach を使うのも手です。

JavaScript
var forEach = Array.prototype.forEach;

var div = document.getElementsByTagName('div');
forEach.call(div, function (elem) {
  elem.style.backgroundColor = '#f00';
});

var section = document.querySelectorAll('section');
forEach.call(section, function (elem) {
  elem.classList.add('fooSection');
});

NodeListHTMLCollection を配列に変換するのも手です。
こうすれば HTMLCollection も静的なものとして扱えます。

JavaScript
var typeOf = function (obj) {
  console.log(Object.prototype.toString.call(obj).slice(8, -1));
};

var div = document.getElementsByTagName('div');

typeOf(div);  // "HTMLCollection"

// 配列に変換する
div = Array.prototype.slice.call(div);

typeOf(div);  // "Array"

div.forEach(function (elem) {
  elem.style.backgroundColor = '#f00';
});

また、ES2015では for...of を使えます。

JavaScript
const div = document.getElementsByTagName('div');
for (const elem of div) {
  elem.style.backgroundColor = '#f00';
}

const section = document.querySelectorAll('section');
for (const elem of section) {
  elem.classList.add('fooSection');
}

DocumentFragment では使えるメソッドが限られている

DocumentFragment 内にある要素を取得しようとした場合、使えるメソッドは getElementById()querySelector()querySelectorAll() のみです。

おまけ:element.childrenNode.childNodes の違い

とある要素の子要素を取得する時、element.childrenNode.childNodes の2つがありますが、これはそれぞれ HTMLCollectionNodeList になります。
どちらもリアルタイムで中身が変わる可能性があることには変わりないのですが、Node.childNodes は余計なノードも含まれるかもしれません。

HTML
<div id="foo">Foo <span>Bar!</span></div>
JavaScript
var foo = document.getElementById('foo');

console.log(foo.children);    // HTMLCollection (1) [span]
console.log(foo.childNodes);  // NodeList (2) ["Foo ", span]

おわりに

今のブラウザの処理速度を考えると、今後は getElementById()querySelector()querySelectorAll() さえ使えればなんとかなりそうですね。

amamamaou
ドット絵を嗜む人です
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした