雰囲気で使ってたから、ちょうどいい機会だしと思い簡単に調べてみたメモ
document.getElementById
- id名で要素を選択して取得する
- idは
#idName
ではなく、idName
Element
を返す- idはHTML上ではユニーク(な筈)仮にidが重複して存在している場合は最初にマッチした要素が返る
- 存在しないidで取得できない場合は
null
が返る document
オブジェクトのみで利用可能- プリミティブだからたぶん高速
HTML
<div id="app"> <h1 id="title"></h1> </div> <div id="main" class="main1"></div> <div id="main" class="main2"></div>
const app = document.getElementById('app'); // => <div id="app"></div> document.getElementById('main'); // => <div id="main" class="main1"></div> document.getElementById('nowhere_guy'); // => null app.getElementById('title'); // => Uncaught TypeError
getElementsByClassName
- クラス名で要素を選択し取得する
- クラス名の指定に
.
不要 HTMLCollection
を返す- 該当する要素が存在しない場合は
HTMLCollection []
を返す。length
は0
- インデックスで要素にアクセスすると
undefined
- "A B"のようにスペース区切りで複数のクラス名を渡した場合は、AかつBのクラスを持つものを取得
document
以外でも使用できる。
rootElem.getElementsByClassName(names)
親要素rootElem
の子要素から該当する要素を探す- 配列のようにインデックスで要素にアクセスできるけど
forEach
などのメソッドを持っていないので、ループで回す場合はfor
を使うか、Array.from
で配列に変換してループで回す
HTML
<div id="app"> <h1 id="title">Get Element</h1> <p class="text red">text1</p> <p class="text blue">text2</p> </div> <p class="small text red">text3</p>
const texts = document.geteElementsByClassName('text'); // => HTMLCollection(3) [p.text.red, p.text.blue, p.small.text.red] console.log( texts.length ); // => 3 console.log( texts[0] ); // => <p class="text red">text1</p> console.log(texts[100]); // => undefined const notFoundClass = document.getElementsByClassName('notfound'); console.log( notFoundClass ); // => HTMLCollection [] console.log( notFoundClass.length ); // => 0 console.log( notFoundClass[0] ); // => undefined const app = documents.getElementById('app'); app.getElementsByClassName('text'); // => HTMLCollection(2) [p.text.red, p.text.blue] const red_text = document.getElementsByClassName('text red'); // => HTMLCollection(2) [p.text.red, p.small.text.red] red_text.forEach((elm) => console.log(elm)); // => getelement.html:42 Uncaught TypeError for(let i=0, l=red_text.length; i<l; i+=1) { console.log( red_text[i].textContent ); } // => text1 // => text3 const red_text_arg = Array.from( red_text ); red_text_arg.forEach((elm) => console.log(elm.textContent)); // => text1 // => text3
querySelector
document.querySelector(selectors);
#idName
,.className
,input[type="text"]
のようなCSSセレクターで取得する要素を指定するElement
を返す- 要素が存在しない場合は
null
を返す - 複数の要素が該当する場合は最初に見つかった要素を返す
targetElem.querySelector()
とした場合は、targetElem
の子要素から要素を探す
HTML
<div id="app"> <h1 class="title">Title</h1> <p class="text red">text1</p> <p class="text blue">text2</p> <div class="content"> <p class="text blue">text3</p> </div> </div>
const app = document.querySelector('#app'); // => <div id="app"></div> app.querySelector('.content .text'); // => <p class="text blue">text3</p> document.querySelector('#notFoundID'); // => null app.querySelector('.text.blue'); // => <p class="text blue">text2</p>
querySelectorAll
- 渡されたCSSセレクターにマッチする要素を集合(
NodeList
)として取得する - Array-likeな
NodeList
オブジェクトを返す - 該当する要素が存在しない場合は
NodeList []
を返す。length
は0
- インデックスで要素にアクセスすると
undefined
targetElem.querySelector()
とした場合は、targetElem
の子要素から要素を探すgetElementsByClassName
で取得できるHTMLCollection
とは違いNodeList
はforEach
メソッドを持っているので、そのままループ処理をすることができる。
for...of...
でもループさせることは出来る。- ※ 当初は
forEach
メソッドも持っていなかったが、使えるようになった - ※
map
,filter
,reduce
,some
などの配列のメソッドは持っていないのでTypeError
になる
- ※ 当初は
HTML
<div id="app"> <h1 class="title">Title</h1> <p class="text red">text1</p> <p class="text blue">text2</p> <div class="content"> <p class="text blue">text3</p> </div> </div> <footer> <p class="small text red">text4</p> </footer>
const textNodeList = document.querySelectorAll('.text'); // => NodeList(4) [p.text.red, p.text.blue, p.text.blue, p.small.text.red] console.log( notFoundNodeList.length ); // => 4 console.log( textNodeList[0] ); // => <p class="text red">text1</p> console.log( textNodeList[100] ); // => undefined const notFoundNodeList = document.querySelectorAll('.notFoundClass'); // => NodeList [] console.log( notFoundNodeList.length ); // => 0 console.log( notFoundNodeList[0] ); // => undefined const app = document.querySelector('#app'); const appTextNodes = app.querySelectorAll('.text.blue'); appTextNodes.forEach((elm) => console.log(elm)); // => <p class="text blue">text2</p> // => <p class="text blue">text3</p> for(let val of appTextNodes) { console.log(val); } // => <p class="text blue">text2</p> // => <p class="text blue">text3</p> appTextNodes.map((elm, i) => elm.innerHTML = `text-${i}`); // => Uncaught TypeError
map
などを使いたい場合は[...obj]
やArray.from()
で配列化してやればOK
const appText = app.querySelectorAll('.text.blue'); [...appText].map((elm, i) => elem.innerHTML = `text-${i}`); // => <p class="text blue">text-0</p> // => <p class="text blue">text-1</p> Array.from(appTextNodes).map((elm, i) => { elm.classList.replace('blue', 'green'); console.log(elm); }); // => <p class="text green">text-0</p> // => <p class="text green">text-1</p>
感想とポエム
querySelector
, querySelectorAll
を使えばjQueryと同じ感覚でHTMLの要素を取得できるようになっているので所謂「脱jQuery」しやすい環境かなという印象です。
querySelector
はjQueryのようにCSSセレクターで要素を取得だったので、複数の要素も取得できそうなイメージを持っていました。実際には最初にマッチした要素1つしか取得できないのでjQuery感覚で覚えてしまっているとハマりどころになりそうです。(複数の要素取得するつもりで、querySelector(.foo).forEach()
ってやっちゃうとエラーになりますし)
後はクラス名で要素を取得しようとした時にナウいquerySelectorAll
で取得できるNodeListオブジェクトはforEach
できるけど、伝統的なgetElementsClassName
で取得できるHTMLCollectionオブジェクトはforEach
したらエラーで死ぬってのもちょっと紛らわしいんで統一感欲しかった感はあります。(querySelector
系使っていけってメッセージなのかもしれないけど…
そして、jQueryとは違い要素が存在していないと、その後のメソッドでエラーになり処理そのものが止まってしまうので、取得した要素が絶対に在る事が担保できる状態で取得した要素を使う必要があるでしょう。
その点jQueryは良くも悪くもjQueryオブジェクトでラップしているので要素が本当に存在しているかどうかを厳密にチェックせずとも雑に使って大丈夫ですし、取得する要素が単数でも複数でもあまり意識する必要が無い。という部分は気にかける部分が減るという意味で利点だと思います。(VDOMのフレームワークで実際のDOM操作を気にかけなくて済むのも、気にかける部分が減るという意味で同じでしょう)
なもんでブラウザのAPIが揃ってきたから、この案件の対応ブラウザの条件ならjQueryは不要ってのは理解できるのだけれど、
jQueryは重いから不要とか、jQueryは辛いからもう使うべきじゃないって、jQueryは悪の枢軸みないなワードを目にすることもありますが、前者は他のライブラリ使ったりwebpackしても、それらの容量あるんだし気になるような大きさじゃなくない?って思うし、後者はたしかに状態管理の為にDOMにデータ紐づけて管理するのは辛いと思うけど、それjQueryそのものじゃなくてDOMに直接イベント付けたりして状態管理する手法が辛いって事じゃない?ってチョット過激なポジショントークのように感じてます。jQuery使ってたらJavaScript覚えられないってのもフレームワークのお作法の範囲で使っていたら、深い部分を理解する必要がないので同じことでしょう。
状態管理するまでもないよう動きつけるだけで、後方互換も重要視されるような"ほうむぺぃじ"製作とかならDOM操作のPolyfillとしてjQueryは別に悪くない選択肢にまだまだ使える場面があり得るよねーって印象で、結局は関わっている案件や作りたいもののに依るんじゃないかなーって感想。
定量的に考えることが得意なはずのエンジニア界隈でライブラリや言語やエディタの話になると宗教問題みたいな論調が幅を利かすのチョット謎。やっぱ思い入れ? (IEの6, 7, 8, 9, 10対応みたいなバージョンがたくさん生きてた時代jQueryには大変お世話になったので悪の枢軸みたいに言われるのチョットなんだなかーって....これは私の思い入れ
[参考]
- Document.getElementById() - Web API | MDN
- Document.getElementsByClassName() - Web API | MDN
- Document.getElementsByTagName() - Web API | MDN
- Document.querySelector() - Web API | MDN
- Document.querySelectorAll() - Web API | MDN
- Array.from() - JavaScript | MDN
- 発売日: 2016/08/09
- メディア: Prime Video
- この商品を含むブログを見る