2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RailsでHTMLCollection/NodeListを使うとき。(JavaScript)

Last updated at Posted at 2019-04-03

RailsでHTMLCollection・NodeListを使う

今回、getElementByClassNameやquerySelectorAllで要素を取得してイベントを登録する際に引っかかったっ点を備忘録として記述しておく。

まず、要素を取得する時点からミスっていたのでその原因の究明から先に行った。

まず、Railsから要素が取得できていなかったので次のようなコードを書いた。

var menuSeconds = document.getElementsByClassName('menu__second');
var profiles    = document.querySelectorAll('.profile-lists li a');
console.log(menuSeconds);
console.log(profiles);

getElementsByClassNameでは戻り値がHTMLCollection、querySelectorAllでは戻り値はNodoListsとなる。

スクリーンショット 2019-04-03 14.20.32.png

ちなみに矢印をクリックすると中の要素が表示されるがHTMLCollectionの中身は全て表示され、lengthも表示される。NodeListsは中身もなく、lengthも0と表示される。
しかし、HTMLCollectionで取得できた要素ですら、いざ使用しようと思うとなぜかできない。
念の為、console.logでそれぞれのlengthを出してみよう。

console.log(menuSeconds.length);
console.log(profiles.length);
スクリーンショット 2019-04-03 14.28.45.png

するとどちらの要素もlengthが0で何も取得できていないことがわかる。

なぜか。簡潔に言うとこれはJavaSctiptがDOMがrenderされる前に走っているから。つまりHTMLのbodyが読み込まれる前にスクリプトが読み込まれているから。Rails上で先にjsファイルが読み込まれていた。

解決策は
1.HTMLのbodyの最後にscriptタグで囲んで書く。
2.下記のようにDOMが読み込まれるのを待つ記述をする。

(function() {
  'use strict';
   document.addEventListener('DOMContentLoaded', function(e) {
     var menuSeconds = document.getElementsByClassName('menu__second');
     var profiles    = document.querySelectorAll('.profile-lists li a');
     console.log(menuSeconds.length);
     console.log(profiles.length);
   });
 })();

のように書くと、
スクリーンショット 2019-04-03 14.40.12.png

上のようにちゃんと取得される。

また、NodeListsから一つの要素を取り出す際は、profile[0]のように配列と同じでいいが、HTMLCollectionから取り出す際は、menuSeconds.item[0]と、itemをつける必要がある。

イベントリスナーを登録したい

取得した要素にイベントリスナーを登録したいときの処理も備忘録のため。

まず前提としてHTMLCollectiondにはforEachは使えない。よってfor文を使おう。

for (var i = 0; i < menuSeconds.length; i++) {
  menuSeconds.item[i].addEventListener('mouseover', function() {

のように書き始めていた。が、

これだとクロージャが生成されずカウンター変数が保持されないためどこから参照してもlengthの値しかカウントされないらしい。

よって中で即時関数を定義することで解決できる。

for (var i = 0; i < menuSeconds.length; i++) { function(n) {
  menuSeconds.item[n].addEventListener('mouseover', function() {
  .
  .
  .
  }, false);
 })(i);

これでちゃんとイベントが登録されているはず。

NodeListsはforEachが使えるので、

profiles.forEach( pf => {
  pf.addEventListener('mouseover', function() {
  .
  .
  .

などとかけば登録できる。

まだまだ未熟者なので至らない箇所があればぜひコメントください。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?