これは何か?
知人からJavaScriptの解説を頼まれたのでせっかくなのでQiitaに残してみよう!という内容です。
何か特定技術に特化した説明はQiitaにも多くありますが、今回は「こういうサンプルコードはこういう風に読み取るよ」というダレトクかわからないけれど、意外とニッチな需要があるのでは?という解説になっています。
今回対象となるコード
<dl>
<dt class="sample">クリックしてね1</dt>
<dd>中身1</dd>
<dt class="sample">クリックしてね2</dt>
<dd>中身2</dd>
<dt class="sample">クリックしてね3</dt>
<dd>中身3</dd>
</dl>
dd {
display: none;
margin: 0 0 20px;
}
.active {
display: block;
}
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をいれて動作確認してみましょう。とにかく一回確認することが大事です。
サンプルコードなんて普通に動かないこともあるので。。
(別にローカルの環境で試してもよいです)
とりあえず実行するとこんな画面が出ます。
「クリックしてね1」をクリックすると展開されます。
もう一度おすと戻ります。
意外とシンプルなアコーディオンの動きですね。
動きがわかったのでコードを読む
もっともSimpleな形にして理解する
JavaScript(に限らず一般的なソースコード)では、ブロックとかスコープ(とか複文とか)いうまとまりがあるのでそれで全体概要を理解してみます。なるべくシンプルな形にしてみて全体の構造を理解するのはコード理解の良い戦略です。
var Accordion_nextEl = (function () {
:
};
}());
:
new Accordion_nextEl(text).showHide();
シンプルな形にしてみるとこれだけのコードです。
According_nextEl
を定義してそれの showHide()
メソッドを呼び出しているだけ、ということがわかります。
さらに細かく見てみます。
実際の具体的な中身は下記のように3つのグループで整理できます。
// Accordion_nextElのコンストラクタの定義 ← つまりは最初に呼び出されるメソッド(関数)の定義
function Accordion_nextEl(el) {
this.el = el;
}
// 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");
});
}
};
// html中の class="sample" の要素を抽出 ※注意! class="sample" の要素はHTML内にたくさんある
var text = document.getElementsByClassName("sample");
// ↑で抽出した要素をコンストラクタにわたしてオブジェクトを作って、showHide() の呼び出し
new Accordion_nextEl(text).showHide();
それでは早速。
最終的に実行されている次の1セットから見ていきます。
var text = document.getElementsByClassName("sample");
new Accordion_nextEl(text).showHide();
少しわかりづらいのですが、この2文以外はすべてクラスや関数の「定義」「宣言」であって最初に無条件に実行されるのはこの2行です。
この2行を丁寧に分解して書くとこのように3つにわけられます。
(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要素 が取得されています。
<dl>
<dt class="sample">クリックしてね1</dt>
<dd>中身1</dd>
<dt class="sample">クリックしてね2</dt>
<dd>中身2</dd>
<dt class="sample">クリックしてね3</dt>
<dd>中身3</dd>
</dl>
(2) new Accordion_nextEl(text)
を見てみます。
これは Accordion_nextEl を 引数text で 生成(new) している処理です。Accordion_nextElには下記のようなコンストラクタが定義されているので生成時にはこのコンストラクタと呼ばれる処理が自動的に呼び出されます。
// コンストラクタの定義 ← つまりは最初に呼び出されるメソッド(関数)の定義
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 は一般的に自分自身と表現します。
ではここで自分自身は誰なのか?
今回のコードは簡単に構造を書くとこうなっています。
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() で使うためにここで持っておくのです。
コンストラクタの処理がおわったので次にいきます。
a.showHide();
showHide()
の実際の中身はこれです。
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
です。
ここまでわかれば後は比較的簡単で1つずつ処理を読んでいけばいいだけです。
外枠のfor文だけ見ると this.el
の個数分、つまり dt の個数分、つまり3回まわっているだけのループです。
2: for (var i = 0; i < this.el.length; i++) {
: :
7: }
そのfor文の中も見てみるとこんな処理です。
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行目のインデントがおかしいですね…… なおしておきます。
サンプルコードも普通の人間が書いてるコードなのでインデントがおかしいくらいいくらもあります!
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文の中身だけをみるとこれです。
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 をつけたり外したりしていることで、「中身1」の表示を切り替えているのです。
トグルとは、ON→OFF、OFF→ON を繰り返すような処理のことを言うエンジニア用語です。 あったら消して、なかったら足す、みたいな処理です。ここでは active があったら消して、なかったら active を足すということをやっています。
では、4行目は何をしているかというと…… this の active クラスをトグルしています、つまり、クリックされたdt の active を付けたり外したりしています。。。が、もともと dt はずっと表示されている(display: block
)なのでたぶんこの4行目は意味がないです。 なくても問題なく動きそうです。
再掲ですが…… サンプルコードも普通に人間が書いたコードなのでいくらでも間違いやおかしいところはあります! サンプルコードだから絶対あってると思わずフラットな目で正しく眺めましょう
ということで 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();
全体の流れを整理すると。
-
var text = document.getElementsByClassName("sample")
で dd の要素を取得 -
new Accordion_nextEl(text)
でオブジェクトの生成してコンストラクタの呼び出し(this.el に dt を格納) -
.showHide()
で dt 要素に「クリックされたらactiveをトグルしてね」という処理を登録(予約)しておく
です。
この後実際に画面上で dd 要素がクリックされることで 3. で登録した処理が動作して active がトグルされる=表示・非表示が切り替わります。
ということで
説明おわり。
クラスの説明など随分はしょってますが、全体の流れを追うという意味ではこのくらいで良いのではないでしょうか??