getElementsByClassNameでforEachがうまくいかなかった

前回の投稿（[document.getElementsByClassNameでうまく取得できない…](https://qiita.com/o-sushi/items/7510774980f5ae2ef7ea)）でgetElementsByClassNameで取得した値を、forEachで回した時に、意図した値が返ってこない原因について、@cfm-art様に凄く分かりやすく教えていただいたので、忘れる前にメモ！！
@cfm-art様ありがとうございます:sparkles:

## getElementsByClassNameで意図しない値が返ってきた

```Javascript:script.js
MODAL.methods = {
    show: function(elm){
        var $self = elm;
        var $next = $self.nextElementSibling;
        if($next.classList.contains(MODAL.tar) && !$self.classList.contains(MODAL.active)) {
            $self.classList.add(MODAL.active);
            $next.classList.add(MODAL.active);

            $next.style.display = 'block';

            console.log(document.getElementsByClassName(MODAL.active));

            MODAL.methods.overlay();
        }
    },
    close: function(){
        var $close = document.getElementById(MODAL.close);
        console.log(document.getElementsByClassName(MODAL.active));
        Array.prototype.forEach.call(document.getElementsByClassName(MODAL.active), function(elm){
            elm.classList.remove(MODAL.active);
            if(elm.classList.contains(MODAL.tar)){
                elm.style.display = 'none';
            }
        });
        $close.remove();
    },
    overlay: function(){
        console.log(document.getElementsByClassName(MODAL.active));
        document.body.insertAdjacentHTML('beforeend', '<div id="' + MODAL.close + '" class="modal-overlay" onClick="MODAL.methods.close();"><span class="modal-overlay_child">close</span></div>');
    }
};
```
### getElementsByClassNameで値を取得した際の各メソッドのlengthの返り値
※lengthの値が2で返ってきてほしい

- MODAL.methods.show = length: 2
- MODAL.methods.overlay = length: 2
- **MODAL.methods.close = length: 1** ←（　＾ω＾）・・・

MODAL.methods.closeだけlengthが1で返ってくる…

しばらく調べてみたものの理由が分からないのでQiitaで質問して、詳しく説明していただきました！

> document.getElementsByClassNameの返却値は配列ではなく「HTMLCollection 」というものです。
> これは大カッコを使って配列のようにアクセス出来る 配列様 オブジェクトですが、配列ではありません。

> MDNには拙い翻訳で以下のように書かれています。

> https://developer.mozilla.org/ja/docs/Web/API/HTMLCollection

>> HTML DOM内のHTMLCollection は生きて(live)います
>> それらは元になったdocumentが変更された時点で自動的に更新されます．

> 以下のようなコードを試してみるとその意味がよく分かるかと思います。

> ```js
/*
 <div class="some-class">要素1件目</div>
 <div class="some-class">要素2件目</div>
 のとき
*/
const list = document.getElementsByClassName('some-class');
console.log(list.length);  // 2
list[0].classList.remove('some-class'); // 先頭の要素のclassを削除
console.log(list.length);  // 1減って1に => getElementsByClassNameしたタイミングではなくリアルタイムのDOMの情報を見ている
```
> つまり件のコードは実質、以下のようになっています。

> ```js
const list = document.getElementsByClassName(MODAL.active);
for (let i = 0; i < list.length; ++i) {
    list[i].classList.remove(MODAL.active);
}
```
> 処理の流れとしては

> ```js
const list = document.getElementsByClassName(MODAL.active);
// for の中身
    // forの1週目
    let i = 0;
    i < list.length; // 0 < 2
    list[i].classList.remove(MODAL.active); // lengthが1減る
    // forの2週目
    ++i;    // 0 → 1
    i < list.length; // 1 < 1 でループを抜ける
```
> となっており、思った結果になっていないと推察できます。

原因は「HTMLCollection」が動的にclassを参照していたことが原因で、ループ中に参照しているclassを削除するとHTMLCollectionの返り値が変化してしまって意図しない結果になってました。

## 解決策
getElementsByClassNameをquerySelectorAllに変更！
HTMLCollectionを返すgetElementsByClassNameではなく、
静的なNodeListを返してくれるquerySelectorAllにすることで解決しました！

```Javascript:script.js
MODAL.methods = {
    show: function(elm){
        var $self = elm;
        var $next = $self.nextElementSibling;
        if($next.classList.contains(MODAL.tar) && !$self.classList.contains(MODAL.active)) {
            $self.classList.add(MODAL.active);
            $next.classList.add(MODAL.active);

            $next.style.display = 'block';

            console.log(document.getElementsByClassName(MODAL.active));

            MODAL.methods.overlay();
        }
    },
    close: function(){
        var $close = document.getElementById(MODAL.close);
        console.log(document.querySelectorAll('.' + MODAL.active));
        Array.prototype.forEach.call(document.querySelectorAll('.' + MODAL.active), function(elm){
            elm.classList.remove(MODAL.active);
            if(elm.classList.contains(MODAL.tar)){
                elm.style.display = 'none';
            }
        });
        $close.remove();
    },
    overlay: function(){
        console.log(document.getElementsByClassName(MODAL.active));
        document.body.insertAdjacentHTML('beforeend', '<div id="' + MODAL.close + '" class="modal-overlay" onClick="MODAL.methods.close();"><span class="modal-overlay_child">close</span></div>');
    }
};
```

querySelectorAllの返り値についてMDNにこう記載されています
> **document.querySelectorAll**
> https://developer.mozilla.org/ja/docs/Web/API/Document/querySelectorAll
> 返り値
> 指定されたセレクタの少なくとも 1 つにマッチする要素について 1 つの Element オブジェクトを含む、生きていない NodeList。

> **NodeList**
> https://developer.mozilla.org/ja/docs/Web/API/NodeList
> 対して、DOM への変更が NodeList オブジェクトの内容に影響を与えないものもあります。document.querySelectorAll() メソッドは、そのような静的な NodeList を返します。

**※最後に**
分からなかったとはいえ、Qiitaで質問してしまい本当に申し訳ないです…
以後気を付けます！
ご指摘ありがとうございました！