Javascript複数アコーディオンのサンプルコードの解説（をベースとしたJSの読み方）

## これは何か？

知人からJavaScriptの解説を頼まれたのでせっかくなのでQiitaに残してみよう！という内容です。

何か特定技術に特化した説明はQiitaにも多くありますが、今回は「こういうサンプルコードはこういう風に読み取るよ」というダレトクかわからないけれど、意外とニッチな需要があるのでは？という解説になっています。

## 今回対象となるコード

```html
<dl>
  <dt class="sample">クリックしてね１</dt>
  <dd>中身１</dd>
  <dt class="sample">クリックしてね２</dt>
  <dd>中身２</dd>
  <dt class="sample">クリックしてね３</dt>
  <dd>中身３</dd>
</dl>
```

```css
dd {
  display: none;
  margin: 0 0 20px;
}
.active {
  display: block;
}
```

```javascript
var Accordion_nextEl = (function () {
    function Accordion_nextEl(el) {
        this.el = el;
    }
    Accordion_nextEl.prototype.showHide = function () {
        for (var i = 0; i < this.el.length; i++) {
            this.el[i].addEventListener('click', function () {
            this.classList.toggle("active");
                this.nextElementSibling.classList.toggle("active");            
            });
        }
    };
    return Accordion_nextEl;
}());
var text = document.getElementsByClassName("sample");
new Accordion_nextEl(text).showHide();
```

**※このコードの良しあしは今回とりあげません！とりあえずこういうコードがネットにあったとしてどうやって解釈するか？という話です。**

## まず動作を確認する（codepenで動作確認）

codepenにJSとCSSとHTMLをいれて動作確認してみましょう。とにかく一回確認することが大事です。
サンプルコードなんて普通に動かないこともあるので。。
（別にローカルの環境で試してもよいです）

とりあえず実行するとこんな画面が出ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/33095/fb8204d2-9594-461e-9365-35ebcdd0040b.png)

「クリックしてね１」をクリックすると展開されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/33095/6c2a6a60-aacc-d2d6-b254-91332a75b8fe.png)

もう一度おすと戻ります。
意外とシンプルなアコーディオンの動きですね。


## 動きがわかったのでコードを読む

### もっともSimpleな形にして理解する

JavaScript（に限らず一般的なソースコード）では、ブロックとかスコープ（とか複文とか）いうまとまりがあるのでそれで全体概要を理解してみます。なるべくシンプルな形にしてみて全体の構造を理解するのはコード理解の良い戦略です。

```javascript
var Accordion_nextEl = (function () {
        :
    };
}());
  :
new Accordion_nextEl(text).showHide();
```

シンプルな形にしてみるとこれだけのコードです。

`According_nextEl` を定義してそれの `showHide()` メソッドを呼び出しているだけ、ということがわかります。

さらに細かく見てみます。
実際の具体的な中身は下記のように３つのグループで整理できます。

```javascript
    // Accordion_nextElのコンストラクタの定義 ← つまりは最初に呼び出されるメソッド(関数)の定義
    function Accordion_nextEl(el) {
        this.el = el;
    }
```

```javascript
    // Accordion_nextElのメソッド(関数)の定義
    Accordion_nextEl.prototype.showHide = function () {
        for (var i = 0; i < this.el.length; i++) {
            this.el[i].addEventListener('click', function () {
            this.classList.toggle("active");
                this.nextElementSibling.classList.toggle("active");            
            });
        }
    };
```

```javascript
// html中の class="sample" の要素を抽出 ※注意! class="sample" の要素はHTML内にたくさんある
var text = document.getElementsByClassName("sample");

// ↑で抽出した要素をコンストラクタにわたしてオブジェクトを作って、showHide() の呼び出し
new Accordion_nextEl(text).showHide();
```

それでは早速。

最終的に実行されている次の１セットから見ていきます。

```javascript
var text = document.getElementsByClassName("sample");
new Accordion_nextEl(text).showHide();
```

> 少しわかりづらいのですが、この２文以外はすべてクラスや関数の「定義」「宣言」であって最初に無条件に実行されるのはこの2行です。

この2行を丁寧に分解して書くとこのように3つにわけられます。

```javascript
(1) var text = document.getElementsByClassName("sample");
(2) var a = new Accordion_nextEl(text)
(3) a.showHide();
```

> `new Accordion_nextEl(text).showHide()` は `new Accordion_nextEl(text)` でオブジェクト生成を行い、生成したオブジェクトの `showHide()` を呼び出すという2つの処理を1行で書いたものなので、2つに分解可能です。

では1行ずつみていきます。

`(1) getElementsByClassName`では **class="sample"**の要素を取得してきています。
気にしておくべきポイントは**class="sample"の要素**はHTML上に**複数**あるということくらいです。
今回のHTMLはこういうものだったのでこの **3つのdt要素** が取得されています。

```html
<dl>
  <dt class="sample">クリックしてね１</dt>
  <dd>中身１</dd>
  <dt class="sample">クリックしてね２</dt>
  <dd>中身２</dd>
  <dt class="sample">クリックしてね３</dt>
  <dd>中身３</dd>
</dl>
```

`(2) new Accordion_nextEl(text)`を見てみます。
これは **Accordion_nextEl** を **引数text** で **生成(new)** している処理です。**Accordion_nextEl**には下記のようなコンストラクタが定義されているので生成時にはこのコンストラクタと呼ばれる処理が自動的に呼び出されます。


```javascript
    // コンストラクタの定義 ← つまりは最初に呼び出されるメソッド(関数)の定義
    function Accordion_nextEl(el) {
        this.el = el;
    }
```

このコンストラクタには引数 `el` があります。
呼び出し元では、`new`時に引数 `text` を渡しているのでこの `el` には `text` が入ってきます。

その後実際の処理である `this.el = el` で、**引数のel** を **thisのel** に代入しています。

> ちなみに **.** は **の** と呼ぶと日本語で理解しやすくなります。 this.el → this **の** el 。
> **引数のel** と **thisのel** は別物であることも注意してください。


ここで **this** が何者なのか？という問題が出てきますが、**this** は一般的に**自分自身**と表現します。
ではここで**自分自身**は誰なのか？

今回のコードは簡単に構造を書くとこうなっています。

```javascript
var Accordion_nextEl = (function () {
    function Accordion_nextEl(el) {
        this.el = el;
    }
    :
}());
```

`Accordion_nextEl` という大きなブロックがあって、そこに `Accordion_nextEl()` コンストラクタがあって、その中で `this` が使われています。この時の `this` が書かれている場所、つまり **自分自身**は**Accordion_nextEl**の大きなブロックを指しています。

**this.el** は **Accordion_nextEl の el** という意味になります。

> ここでの`Accordion_nextEl`のブロックを **クラス** と呼びます。
> `var a = new Accordion_nextEl(text)` で生成(new)された `a` を **インスタンス** と呼びます。
> 今回の例では **this** は **インスタンス** のことを指しています。

意味はわかったとしても、ここで `this.el` というのが急に出てきているので何か特殊なものに感じますが……　まったく特殊ではありません。単なる変数です。（`this`には特殊な意味がありますが） `el` は単なる変数です。もちろん別に `el` という名前である必要もなく `this.a` でも `this.b` でも何でもいいです。

JavaScriptの慣習上で要素（element）を扱う変数名は **e** や **el** などが良く使われるのでその名前を使っているだけです。

長くなりましたが、改めてこのコンストラクタで何をしているかと言うと、``this.el`` つまり **Accordion_nextElのel変数** に **class="sample"の要素** を持っておこうというだけです。

> この先に使う値をコンストラクタで格納しておいて、後でメソッド呼び出し時に使う、というのはオブジェクト指向的なプログラムの定石です！ 今回で言えば、後で呼び出される showHide() で使うためにここで持っておくのです。

コンストラクタの処理がおわったので次にいきます。

```javascript
a.showHide();
```

`showHide()`の実際の中身はこれです。

```javascript
1:    Accordion_nextEl.prototype.showHide = function () {
2:        for (var i = 0; i < this.el.length; i++) {
3:            this.el[i].addEventListener('click', function () {
4:            this.classList.toggle("active");
5:                this.nextElementSibling.classList.toggle("active");            
6:            });
7:        }
8:    };
```

さっそく2行目に `this.el` が出てきていますね。
コンストラクタで **class="sampleの要素"** を格納していた、あの `this.el` です。

ここまでわかれば後は比較的簡単で１つずつ処理を読んでいけばいいだけです。

外枠のfor文だけ見ると `this.el` の個数分、つまり dt の個数分、つまり3回まわっているだけのループです。

```javascript
2:        for (var i = 0; i < this.el.length; i++) {
:              :
7:        }
```

そのfor文の中も見てみるとこんな処理です。

```javascript
2:        for (var i = 0; i < this.el.length; i++) {
3:            this.el[i].addEventListener('click', function () {
4:            this.classList.toggle("active");
5:                this.nextElementSibling.classList.toggle("active");            
6:            });
7:        }
```

おっと…… 4行目のインデントがおかしいですね…… なおしておきます。

> サンプルコードも普通の人間が書いてるコードなのでインデントがおかしいくらいいくらもあります！

```javascript
2:        for (var i = 0; i < this.el.length; i++) {
3:            this.el[i].addEventListener('click', function () {
4:                this.classList.toggle("active");
5:                this.nextElementSibling.classList.toggle("active");            
6:            });
7:        }
```

インデントがなおったところで解説。

for文の中身だけをみるとこれです。

```javascript
3:            this.el[i].addEventListener('click', function () {
4:                this.classList.toggle("active");
5:                this.nextElementSibling.classList.toggle("active");            
6:            });
```

`i` はとりあえず最初の値の `0` と仮定して読むと `this.el[0]` は `this.el` の最初の要素になるので、HTMLの `dt` の最初の要素を指していることになります。

`this.el[0]` の `addEventListener('click'...` を呼び出すことで、 `dt` をクリックした処理を登録しています。

クリックしたときの処理としては 4～5行目 の処理になります。

さてこの4～5行目でまた **this** が出てきました。

この **this** は何でしょう？ **this**は**自分自身**のことなのですが、ここで言う自分自身とは誰でしょう？
正解は`dt`です。 今見ている処理は `dt` がクリックされたときの処理なので、**this** は **クリックされたdt自身** を指しています。

それを踏まえて5行目は何をしているかというと、**this** つまり **クリックされたdt** の **nextElementSibling** の **active** をトグルしています。

ここの **nextElementSibling** とは **dt** の隣接する次の要素を指すので **クリックされたdt** の次の行の **dd** を指しています。この **dd** にトグルで **active** をつけたり外したりしていることで、「中身１」の表示を切り替えているのです。

> トグルとは、ON→OFF、OFF→ON を繰り返すような処理のことを言うエンジニア用語です。 あったら消して、なかったら足す、みたいな処理です。ここでは active があったら消して、なかったら active を足すということをやっています。

では、4行目は何をしているかというと…… **this** の **active** クラスをトグルしています、つまり、**クリックされたdt** の **active** を付けたり外したりしています。。。が、もともと **dt** はずっと表示されている（`display: block`）なのでたぶんこの4行目は意味がないです。 なくても問題なく動きそうです。

> 再掲ですが…… サンプルコードも普通に人間が書いたコードなのでいくらでも間違いやおかしいところはあります！ サンプルコードだから絶対あってると思わずフラットな目で正しく眺めましょう

ということで JavaScript のコードを全て再掲します。

```javascript
var Accordion_nextEl = (function () {
    function Accordion_nextEl(el) {
        this.el = el;
    }
    Accordion_nextEl.prototype.showHide = function () {
        for (var i = 0; i < this.el.length; i++) {
            this.el[i].addEventListener('click', function () {
            this.classList.toggle("active");
                this.nextElementSibling.classList.toggle("active");            
            });
        }
    };
    return Accordion_nextEl;
}());
var text = document.getElementsByClassName("sample");
new Accordion_nextEl(text).showHide();
```

全体の流れを整理すると。

1. `var text = document.getElementsByClassName("sample")` で dd の要素を取得
2. `new Accordion_nextEl(text)` でオブジェクトの生成してコンストラクタの呼び出し（this.el に dt を格納）
3. `.showHide()` で dt 要素に「クリックされたらactiveをトグルしてね」という処理を登録（予約）しておく

です。

この後実際に画面上で dd 要素がクリックされることで 3. で登録した処理が動作して active がトグルされる＝表示・非表示が切り替わります。


## ということで

説明おわり。

クラスの説明など随分はしょってますが、全体の流れを追うという意味ではこのくらいで良いのではないでしょうか？？