【JavaScript】DOM 要素取得メソッド、結局どれ使う?

it
troubled-woman

DOM操作の要素取得メソッドが多くて、どれを使えば良いのかわからない

この声にお答えします。

DOM操作するためのメソッドは何個もあります。
そのため同じ要素を取得できるメソッドが複数あり、どれを使えば良いかわからない時はありませんか。

本記事ではどういうDOM操作のメソッドがあって、それぞれをどう使えば良いのか見ていきます。

対象読者

  • どの場面でどの要素取得メソッドを使用すべきか知りたい人
  • querySelectorAllgetElements* の違いを知りたい人
  • 要素取得後に forforEach のどちらを使えるか知らない人

それでは早速やっていきましょう

要素の取得方法のベストプラクティス

どの場面でどのメソッドを使用すべきか見ていきます。

本記事で取り上げるメソッドは以下です。

getElementById()
idが指定した文字列に一致する要素を取得する
getElementsByClassName()
指定したクラス名を全て持つ要素を全て取得する
getElementsByTagName()
タグ名が指定した文字列に一致する要素を全て取得する
getElementsByName()
nameが指定した文字列に一致する要素を全て取得する
querySelector()
指定したCSSセレクタに該当する要素を取得する
querySelectorAll()
指定したCSSセレクタに該当する要素を全て取得する

実行にかかる時間の順番を早い順に並べると、
getElemensBygetElemensBy* < querySelector < querySelectorAll
となるみたいです。(実行速度は公式ドキュメントに記載されているわけではなく、他サイトから拾ってきた情報をもとに推測しています)

なので、実行速度面で考えるベストプラクティスは以下になりそうです。

  1. まず id で要素を指定できるなら getElementById を使い、
  2. id で指定できない場合
    タグで指定できるなら getElementsByTagName
    クラスで指定できるなら getElementsByClassName
    name 属性で指定できるなら getElementsByName を使う
  3. 上記のいずれでも指定できない要素を取得したい時は querySelectorquerySelectorAllを使う

ただ実行速度はメソッドを1000回実行してもms程度です。
なので、よほどDOM操作を多用していたり、大規模なHTMLでなければ、実行速度の違いは感じづらいと思います。

個人的には、getElementById で要素を取得できるか考えて、取得できなければ思考停止で querySelectorquerySelectorAll を使い回していこうと思っています。

実践:要素の取得

状況別に要素の取得で使用できるメソッドをまとめました。

以下のHTMLを仮定します。


<h2 class="itemsList">商品一覧</h2>
<section id="newItems">
<h3>新商品</h3>
    <p class="item new">新商品A</p>
    <p class="item new">新商品B</p>
</section>
<section id="saleItem">
    <h3>セール中の商品</h3>
    <p class="item sale">セール商品A</p>
</section>
<section id="popularItem">
    <h3>人気商品</h3>
    <p class="item popular">人気商品A</p>
</section>
<div>
    <button type="button" name="buy"value="100">購入ボタン</button>
    <button type="button" name="cancel"value="0">キャンセル</button>
</div>
<form>
    <legend>連絡方法</legend>
    <fieldset>
        <div>
            <label for="option1">メール</label>
            <input type="radio" id="option1" name="contact" value="email">
            <label for="option2">電話</label>
            <input type="radio" id="option2"name="contact"value="phone">
        </div>
        <div>
            <button type="submit">送信</button>
        </div>
    </fieldset>
</form>

取得する要素が一つの時

idで要素を指定できる時getElementById を使う

document.getElementById("newItems");

CSSセレクタでしか要素を指定できない時は、queySelector を使う

document.querySelector("#newItems .item:last-child")

タグ名で取得できる時querySelectorgetElementsByTagName を使う


document.querySelector("h2")
// 実行速度で言うと以下の方が早いと思われる
document.getElementsByTagName("h2")
      

クラス名で取得できる時querySelectorgetElementsByClassName を使う


document.querySelector(".itemsList")
// 実行速度で言うと以下の方が早いと思われる
document.getElementsByClassName("itemsList")
      

name で取得できる時querySelectorgetElementsByName を使う。


document.querySelector("[name='buy']")
// 実行速度で言うと以下の方が早いと思われる
document.getElementsByName("buy")
          

取得する要素が複数の時

クラス名で複数の要素を取得する時querySelectorAllgetElementsByClassName を使う


document.querySelectorAll(".item.new")
// 実行速度で言うと以下の方が早いと思われる
document.getElementsByClassName("item new")
      

タグ名で複数の要素を取得する時querySelectorAllgetElementsByTagName を使う


document.querySelectorAll("h3")
// 実行速度で言うと以下の方が早いと思われる
document.getElementsByTagName("h3")
          

name で複数の要素を取得する時querySelectorAllgetElementsByName を使う


document.querySelectorAll("[name='contact']")
// 実行速度で言うと以下の方が早いと思われる
document.getElementsByName("contact")

実践:複数の要素を取得後の、各要素へのアクセス方法

querySelectorAllgetElementsBy* メソッドでは複数の要素を取得できます。その後の各要素へのアクセス方法についての解説です。

特定の要素にアクセスする

要素取得のメソッドがquerySelectorAll の時も、getElementsBy* の時も同じ方法で特定の要素にアクセスできます。


const newItems = document.querySelectorAll(".item.new")
// 以下いずれかの方法で特定の要素にアクセスできる。
newItems.item(0);
newItems[0];
      

const newItems = document.getElementsByClassName("item new");
// 以下いずれかの方法で特定の要素にアクセスできる。
newItems.item(0);
newItems[0];
      

取得した要素全てにアクセスする

取得した要素全てにアクセスしたい場合は、ループ処理を行います。
取得時に使用したメソッドによって処理の方法は変わってきます。 (これは HTMLCollectionNodeList の違いによるところです。後述の補足で解説します)

querySelectorAllgetElementsByName で取得した要素は forEach でループ処理を行えます。


// querySelectorAll
const newItems = document.querySelectorAll(".item.new")
newItems.forEach(newItem => {
    console.log(newItem);
})
      

// getElementsByName
const contact = document.getElementsByName("contact")
contact.forEach(item => {
  console.log(item)
})
      

getElementsByClassNamegetElementsByTagName の時は forEach は使えません。
代わりに for...offor を使います。


// getElementsByClassName
const newItems = document.getElementsByClassName("item new");
for(const newItem of newItems){
  console.log(newItem);
}
for(let i=0; i < newItems.length; i++){
  console.log(newItems[i]);
}
      

// getElementsByTagName
const heading3s = document.getElementsByTagName("h3")
for (const heading3 of heading3s) {
  console.log(heading3);
}
for(let i=0; i < heading3s.length; i++){
  console.log(heading3s[i]);
}
      

補足:HTMLCollection と NodeListの違いについて

HTMLCollectionNodeList は配列風のオブジェクトです。
配列風であって配列ではないのがポイントです。

HTMLCollection もしくは NodeList が取得されるメソッド

要素取得時に使用するメソッドによって、HTMLCollection が取得される場合と NodeList が取得される場合があります。

HTMLCollection は以下のようなメソッドで取得されます。
  • getElementsByClassName
  • getElementsByTagName
NodeList は以下のようなメソッドで取得されます。
  • querySelectorAll

getElementsByName では基本的に NodeList が取得されますが、IEやEdgeでは HTMLCollection が取得されるようです。

特徴の違い

具体的な HTMLCollectionNodeList の違いについてですが、以下になります。

  • 要素が増減した時の違い

    HTMLCollection要素数の変更が反映されます。
    これはライブオブジェクトと呼ばれる性質のオブジェクトだからです。

    NodeList は基本的には要素数が変更しても反映されません。(ただし中には NodeList でもライブオブジェクトの性質を持つものもあります。例えば、childNodes プロパティの返り値などです。)

  • 使用できるメソッドの違い

    HTMLCollection よりも NodeList の方が使えるメソッドが比較的多いです。
    例えば、forEach メソッドは NodeList にはありますが HTMLCollection にはありません。
    逆に、HTMLCollection では使えるが NodeList では使えないメソッドもあるが、少ない( namedItemメソッド)。

まとめ

querySelectorquerySelectorAll で大体の要素取得がまかえます。

getElementsBy*を使う理由は実行速度が早い、要素数の変更が動的に反映されるといったところでしょうか。

本記事の筆者は、getElementsBy* の恩恵をまだ肌で感じたことはないので、とりあえず querySelector を使っていこうと思います。

コメント