今更ながらJavaScriptの復習をしてみる 5. jQueryを使ってブラウザ情報を取得して操作する。

# はじめに
[前回](https://qiita.com/myktmht/items/a6af3325b0785310d074)に引き続き、JavaScript(以下JS)の復習を行なっていきます。
今回は、jQueryというライブラリを使って基本的なブラウザ情報とイベントを実装していきます。

# jQueryでタブメニュー切り替えを実装する。
[第3回の応用編](https://qiita.com/myktmht/items/04da17d84e67cf740ec6#%E5%BF%9C%E7%94%A8%E7%B7%A8)で使用したJSを書き換えて、jQueryでタブメニューの切り替えを記述していきます。

[![Image from Gyazo](https://i.gyazo.com/a50209c0235552ce0cae380b3abb7334.gif)](https://gyazo.com/a50209c0235552ce0cae380b3abb7334)

```js:JavaScriptの場合
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = document.getElementsByClassName("menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除（"[0]は、最初の要素の意味"）
    document.getElementsByClassName("active")[0].classList.remove("active");

    // クリックしたタブにactiveクラスを追加
    this.classList.add("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    document.getElementsByClassName("show")[0].classList.remove("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcoutentクラスにshowクラスを追加する
    document.getElementsByClassName("content")[index].classList.add("show");
  }

  // タブメニューの中でクリックイベントが発生した場所を探し、下で定義したtabSwitch関数を呼び出す
  tabsAry.forEach(function(value) {
    value.addEventListener("click", tabSwitch);
  });
});
```

JSだとこれだけ長いコードになるが、jQueryを使用することで短いコードで同様の動作を行うことができる。

```js:jQueryの場合
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除（"[0]は、最初の要素の意味"）
    $('.active').removeClass("active");

    // クリックしたタブにactiveクラスを追加

    $(this).addClass("active");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabs.index(this);

    // クリックしたタブに対応するshowクラスを追加する
    $(".content").removeClass("show").eq(index).addClass("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す
  tabs.click(tabSwitch);    
});
```

もし、JSに関して忘れた内容があれば、[第3回](https://qiita.com/myktmht/items/04da17d84e67cf740ec6)に戻って復習してみてください。
また、JSやjQueryで用意されている関数やメソッドはリファレンス（参考文献）を確認するのが一番確実です。
どんな文法があるのか知識を増やすためにも、リファレンスをいつも見る癖をつけると良いです。

## jQueryを導入
jQueryを使用するには、jQueryのライブラリを読み込む必要がある。
以下のリンク先にjQueryを読み込む記述があります。
[jQueryへのリンク](https://developers.google.com/speed/libraries/#jquery)
リンク先のページをスクロールするとjQueryという見出しと、その下に**3. x snippet**という記述が見つかる。3. x snippetの下に`script`タグに囲まれた記述があるので、この記述をコピーする。

**:warning:コピーした記述と記事の記述が若干異なるかもしれませんが、jQueryのバージョンが違うので問題はありません。**

[![Image from Gyazo](https://i.gyazo.com/440536b566f4d5906cbd40d5fa18cdfd.png)](https://gyazo.com/440536b566f4d5906cbd40d5fa18cdfd)

コピーした記述をindex.htmlの`head`内に以下のように貼り付けることで、jQueryを読み込むことができる。

**:warning:jQueryは他のJSファイルよりも先に読み込まないとエラーが発生する。そのため、JSファイルよりも上にjQueryの読み込みを記述する。今回はmain.jsに読み込みよりも上に記述をする。**

```index.html
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="stylesheet/style.css">
  <link rel="stylesheet" type="text/js" href="js/index.js">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
  <script src="./main.js"></script>
  <title>Tab Menu Sample</title>
</head>
```

main.jsの下にjQueryの記述を追加し、以降の作業を進めるとjQueryの読み込み前に関数を実行してしまうため、画像のようなエラーが発生します。**

[![Image from Gyazo](https://i.gyazo.com/0007bb18d09ffc110ae0baea80634bc6.png)](https://gyazo.com/0007bb18d09ffc110ae0baea80634bc6)


## HTML要素を取得する
jQueryでのHTML/CSSの操作はJSと同じ。
1. HTML要素を取得する
2. 取得したHTMLを操作する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414101/be3915ac-1ffd-5fb2-d5c2-01e215fb109e.png)

### main.jsを以下のように編集し、HTML要素を取得する

menu_itemクラス要素を取得するために、以下の記述をmain.jsの一番下に追記する。

```js:main.js
$(function() {
  console.log($(".menu_item"));
});
```

追記できたらindex.htmlをブラウザで表示して、開発ツールのコンソールを確認。(command + option + i)
コンソールを確認すると画像のように出力されるはず。

[![Image from Gyazo](https://i.gyazo.com/d921e95bd8ed13fa5266209decf49732.png)](https://gyazo.com/d921e95bd8ed13fa5266209decf49732)

コンソールに出力されているのは、**クラス属性の値がmenu_itemのHTML要素**の情報。
また、JSの`document`にあたるのが、`$(ドルマーク)`。
`$`のあとに**セレクタ**と呼ばれるHTMLの指定方法を記述することで、HTML要素が取得できる。

```js:HTML要素の取得
$("セレクタ名")
```
jQueryは様々なセレクタを用意している。よく使用するセレクタは以下の4つ

・ IDセレクタ
・ クラスセレクタ
・ 要素セレクタ
・ 属性セレクタ

### IDセレクタ
HTMLのid属性で指定するセレクタのこと。
IDセレクタは、取得したいHTML要素の`id属性に#(ハッシュ)を付けたもの`をセレクタとして利用する。

```js:IDセレクタの例
$("#idSelector") // idがidSelectorの要素を取得
```

#### クラスセレクタ
HTML要素のclass属性で指定するセレクタのこと。
クラスセレクタは、取得したいHTML要素の`class属性の値に.(ドット)を付けたもの`をセレクタとして利用する。

```js:クラスセレクタの例
$(".classSelector") // classがclassSelectorの要素をすべて取得
```

#### 要素セレクタ
h1やpのようなHTML要素を対象としてセレクタのこと。
クラスセレクタは、取得したいHTML要素の`属性名をそのまま`セレクタとして利用する。

```js:要素セレクタの例
$("h1") // h1要素をすべて取得
```

#### 属性セレクタ
HTMLのタグの属性値を指定したい場合、`$("[ 属性 = '値' ]")`で属性セレクタを取得できる。

```js:属性セレクタの例
$("input[ type='radio' ]");  // <input type="radio">のHTML要素を取得する
```

##### **要するに、JavaScriptで書いていたDOM要素の取得を全て共通の`$("セレクタ名")`で書き換えることができる！**

 **:warning: セレクタを指定した場合、相当する全てのHTML要素が取得される。そのため、idセレクタは1つしか要素が取得されないが(id属性の値は被ることがないため)、他のセレクタでは複数の属性が取得される可能性がある。**

### JSのセレクタ取得部分を全てjQueryで書き換える

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = document.getElementsByClassName("menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除（"[0]は、最初の要素の意味"）
    document.getElementsByClassName("active")[0].classList.remove("active");

    // クリックしたタブにactiveクラスを追加
    this.classList.add("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    document.getElementsByClassName("show")[0].classList.remove("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcoutentクラスにshowクラスを追加する
    document.getElementsByClassName("content")[index].classList.add("show");
  }

  // タブメニューの中でクリックイベントが発生した場所を探し、下で定義したtabSwitch関数を呼び出す
  tabsAry.forEach(function(value) {
    value.addEventListener("click", tabSwitch);
  });
});
```
↓

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除（"[0]は、最初の要素の意味"）
    $(".active")[0].classList.remove("active");

    // クリックしたタブにactiveクラスを追加
    this.classList.add("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    $(".show")[0].classList.remove("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcoutentクラスにshowクラスを追加する
    $(".content")[index].classList.add("show");
  }

  // タブメニューの中でクリックイベントが発生した場所を探し、下で定義したtabSwitch関数を呼び出す
  tabsAry.forEach(function(value) {
    value.addEventListener("click", tabSwitch);
  });
});
```

一文が短くて見やすいものになりましたね。

## クリックイベントを実装
クリックイベントの取得は`click()`を使用。
JSの場合、クリックイベントが発生した場所を確認するため、forEach文を使って配列の中身を1つ1つ確認していた。

```js:JavaScriptの場合
// タブのDOM要素を取得し、変数で定義
let tabs = $(".menu_item");
// tabsを配列に変換する
tabsAry = Array.prototype.slice.call(tabs);

function  tabSwitch(){
    // 省略
}

tabsAry.forEach(function(value) {
　　// タブメニューの中でクリックイベントが発生した場所を探し、下で定義したtabSwitch関数を呼び出す
　　value.addEventListener("click", tabSwitch);
});
```
例えて言うなら、タンスから目的の物を見つけるために毎回1段1段開けて確認する状態。

jQueryで`click()`を使用する場合、セレクタを指定すればクリックした要素の値を簡単に取り出してくれる。
よって、配列に変換する必要もなく、以下のようにすっきりとしたコードに書き換えられる。

```js:jQueryの場合
// タブのDOM要素を取得し、変数で定義
let tabs = $(".menu_item");
// tabsを配列に変換する
tabsAry = Array.prototype.slice.call(tabs);

function  tabSwitch() {
    // 省略
}

tabs.click(tabSwitch);  
```

これで、変数tabsに入っているmenu_itemクラスの中身がクリックイベントが発生したら、tabSwitch関数を呼び出してくれる。
さらに、クルックされた要素を特定する必要がある。`this`を使うことで、イベントが発生した要素を取得できる。

#### this
**関数の中でthisを使う**と、イベントの発生元となった要素を取得することができる。thisは、使用する場面によって取得できるものが異なる。
ここでは全ての用途の説明は割愛するが、`this`は**JSで、非常によく使用される。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414101/839f3943-ef5a-dfc8-618d-8e5d882d3ea2.png)

実際にクリックした要素を取得できるか確認するために、`tabSwitch`と言う関数の中に`console.log(this);`を加えてクリックした要素を取得できるか確認。

```js:main.js
function tabSwitch(){
  console.log(this);
   // 処理
}
```

以下の画像のように、コンソール上にクリックした`aタグ`を取得し、出力する。

[![Image from Gyazo](https://i.gyazo.com/f2c7f9db638e49468fd54ab4bcca5b04.gif)](https://gyazo.com/f2c7f9db638e49468fd54ab4bcca5b04)

`this`はJSの機能。そのため`this`で取得した要素は、JSで使用するDOM要素になっている。
jQueryで使う場合は、`$(this)`と指定しないとエラーになる。

#### ① "tabs.click(tabSwitch)"で引数のtabsSwitch関数を呼び出す。
#### ② 「tabsSwitch関数の中のthis」=「クリックした要素を取得する」と言う意味

`console.log`はデバック用で書いたので、コンソール上で正しくDOMを取得できているのを確認したら、削除する。

## クラスの操作をjQueryで書き換える
JSでは`classList`を使用して、クラスの追加や削除をしていたが、jQueryはクラスを追加・削除するメソッドが用意されている。

メニュータブの切り替えの実装についてのおさらい
例）「Ruby」のタブをクリックした場合

① タブのデザインが適用されている"activeクラス"を削除する
② クリックされたタブに"activeクラスを追加する"
③ "showクラス"を削除する
④ "menu_itemクラス"からクリックしたタブと対応する"showクラス"を追加する

[![Image from Gyazo](https://i.gyazo.com/0172e54ff043fabae838b11727e755e6.gif)](https://gyazo.com/0172e54ff043fabae838b11727e755e6)

HTMLのactiveクラスとshowクラスにどんなCSSがあたっているのかは、style.cssの中身を直接確認する。
①~④の手順でタブ機能を実装する。

### ①クリックしたタブのクラス要素を削除する
HTMLのactiveクラスを削除するために、jQueryで用意されている`removeClass()`を使用する。

#### removeClass()
JSは`classList.remove()`と言う書き方をしていたが、jQueryは`removeClass()`というメソッドが用意されている。
`classList.remove()`と違い`[0]`と指定しなくても、最初の要素を指定してくれる。

以下のように編集する。

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除
    $(".active").removeClass("active");
    
    // クリックしたタブにactiveクラスを追加
    this.classList.add("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    $(".show").removeClass("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcontentクラスにshowクラスを追加する
    $(".content")[index].classList.add("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す。
  tabs.click(tabSwitch);
});
```

### ②クリックしたタブにクラス要素を追加する
JSで書いた新しいクラスを追加する`classList.add()`と同じように、jQueryも`addClass()`というメソッドがある。

#### addClass()
`addClass()`も`removeClass()`と同様に`[0]`と要素を指定する必要がない。
また、`this`をjQueryで使える文法に変える必要があるため、`$(this)`に書き換える。
thisを書き換えない場合はタブは反応せず
`Uncaught TypeError: this.addClass is not a function`とコンソールに表示される。

以下のようにコードを編集する。

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除
    $(".active").removeClass("active");
    
    // クリックしたタブにactiveクラスを追加
    $(this).addClass("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    $(".show").removeClass("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcontentクラスにshowクラスを追加する
    $(".content")[index].classList.add("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す。
  tabs.click(tabSwitch);
});
```

これでクラスが追加できるようになったので、挙動を確認する。
`$(".content")[index].classList.add("show");`はJSの書き方だが、現時点ではこのままにしておく。

## コードをさらにリファクタリングする
jQueryで書けるところを全て書き換えて見やすいコードに編集する。

下記の配列と`indexOf()`に注目

```js:main.js
// tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

// 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);
```

JSだけで書く場合、`.manu_items`のような集合したDOM要素のうち、何番目の要素がクリックされたかを取得するために、`indexOf()`を使用する必要があった。
しかし、`indexOf()`は配列に対してだけ使えるので、集合したDOM要素を配列に変換する必要があった。
この部分のコードを短くするために、jQueryで用意されている`index()`を使用する。

### index()
`index()`は配列に変換しなくても、集合したDOM要素から引数に指定したDOMと同じ要素番号を戻す。

```js:index()の書き方
index("セレクタ") // セレクタと一致したDOMの要素番号を戻す
```

`index()`を使用すれば配列に変換する必要がなくなるので、以下のように編集できる。

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除
    $(".active").removeClass("active");
    
    // クリックしたタブにactiveクラスを追加
    $(this).addClass("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    $(".show").removeClass("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabs.index(this);

    // クリックしたcontentクラスにshowクラスを追加する
    $(".content")[index].classList.add("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す。
  tabs.click(tabSwitch);
});
```

### eq()
引数を要素番号に取り、集合したDOM要素から選択できる。

```js:eq()の書き方
eq()  // セレクタと一致したDOMの要素番号を戻す
```

以下のように編集して書き換える。

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除
    $(".active").removeClass("active");

    // クリックしたタブにactiveクラスを追加
    $(this).addClass("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    $(".content").removeClass("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabs.index(this);

    // クリックしたタブに対応するshowクラスを追加する
    $(".content").eq(index).addClass("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す
  tabs.click(tabSwitch);    
});
```

これで、`addClass`を使ってshowクラスの追加ができるようになった。
さらに、contentクラスからshowクラスを削除するのは、クラスを追加するのと一緒に一行で書くことができる。

```js:main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除
    $(".active").removeClass("active");
    
    // クリックしたタブにactiveクラスを追加
    $(this).addClass("active");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabs.index(this);

    // showクラスを削除した後、クリックしたcontentクラスにshowクラスを追加する
    $(".content").removeClass("show").eq(index).addClass("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す。
  tabs.click(tabSwitch);
});
```

これで③~④の「showクラスを削除した後、contentクラスからクリックしたタブの要素番号を指定して、showクラスを追加する」という記述を一行で表すことができた。

### HTMLの読み込みをjQueryで書く

HTMLの読み込みが終わってからscriptタグを読み込むために、最初に以下の記述を使っていた。

```js:JSの場合
window.addEventListener("load", function() {
  // 処理
});
```

jQueryの場合、以下のように書くことができる

```js:jQueryの場合
$(function()  {
   // 処理
}
```

これの方が記述が簡単なので、書き換える。

```js:main.js
$(function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = $(".menu_item");

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除
    $(".active").removeClass("active");
    
    // クリックしたタブにactiveクラスを追加
    $(this).addClass("active");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabs.index(this);

    // showクラスを削除した後、クリックしたcontentクラスにshowクラスを追加する
    $(".content").removeClass("show").eq(index).addClass("show");
  }

  // タブがクリックされたらtabSwitch関数を呼び出す。
  tabs.click(tabSwitch);
});
```


### 最後にもう一度、実装する流れをおさらいすると...

例）「Ruby」のタブをクリックした場合

① タブのデザインが適用されている"activeクラス"を削除する
② クリックされたタブに"activeクラスを追加する"
③ "showクラス"を削除する
④ "menu_itemクラス"からクリックしたタブと対応する"showクラス"を追加する

この①~④の「ビジネスロジックを自分で組む立てる力」がエンジニアにとってとても大事。
そのために、分からないことをいきなり検索せず、紙などにどういった流れで実装すれば、実現することができるか仮説を立ててから調べるようにすると良い。

# 今回はここまで
[次回] (#)に続く（制作中）