連動するメニューをJavaScriptで作る方法


以下のような大科目の下に小科目があるようなデータがある場合に、大科目を選ぶとそれに対応する小科目が表示されるメニューを作成してみました。ウェブ上では、あらかじめHTMLファイルの`<option>`タグに記載するパターンが多かったので、必要な値はJavaScriptの変数に格納する方法でやってみました。

```
科目
┣食費
┃　┗生鮮食品,中食,外食,飲料,お菓子,加工品
┣光熱費
┃　┗電気代,ガス代,水道代,灯油代
┣医療費　　
┃　┗治療費,処方箋薬,市販薬
┣娯楽費
┃　┗パチンコ代,旅行代,お土産代,漫画
┣教育費
┃　┗授業料,給食費,塾代,教科書代,書籍代
┗その他
　　┗冠婚葬祭費,タクシー代,家賃,税金,仕送り
```

まず、以下のHTMLファイルを作成

```HTML:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>javascript lesson</title>
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="form-group">
        <label for="account1">大科目を選択してください</label>
        <select class="form-control" id="account1" name="account1">
        </select>
      </div>

      <div class="form-group">
        <label for="account2">小科目を選択してください</label>
        <select class="form-control" id="account2" name="account2">
        </select>
      </div>
    </div>
  </div>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
  <script src="index.js"></script>
</body>
</html>
```
次に、以下のJavaScriptファイルを作成

```Javascript:index.js
(function() {
  var Acc1, accounts1, i, j, len;

  accounts1 = [
  "科目を選択", 
  "食費", 
  "光熱費", 
  "医療費", 
  "娯楽費", 
  "教育費", 
  "その他"];

  j = 0;

  for (i = 0, len = accounts1.length; i < len; i++) {
    Acc1 = accounts1[i];
    $('#account1').append("<option value=" + j + ">" + Acc1 + "</value>");
    j += 1;
  }

  $('#account1').on('change', function() {
    var Acc2, accounts2, index, k, len1, ref;
    accounts2 = [
    ["科目を選択"], 
    ["生鮮食品", "中食", "外食", "飲料", "お菓子", "加工品"],
    ["電気代", "ガス代", "水道代", "灯油代"],
    ["治療費", "処方箋薬", "市販薬"], 
    ["パチンコ代", "旅行代", "お土産代", "漫画"], 
    ["授業料", "給食費", "塾代", "教科書代", "書籍代"], 
    ["冠婚葬祭費", "タクシー代", "家賃", "税金", "仕送り"]
    ];
    index = $('#account1').val();
    $('#account2').empty();
    ref = accounts2[index];
    for (k = 0, len1 = ref.length; k < len1; k++) {
      Acc2 = ref[k];
      $('#account2').append("<option>" + Acc2 + "</option>");
    }
    return this;
  });

}).call(this);
```
処理の流れは以下のとおりです。

1. 大科目のリストをaccount1変数に格納する
1. HTMLファイルを開いた際に、account1変数の値を「ID："account1"」のメニューにセット。このとき、小科目の絞り込みに使うためのインデックスもvalue属性にセットしておく。
1. 小科目のリストをaccount2変数に格納する。大科目のインデックスを使って絞り込みを行うので、大科目の順番に沿った２次元配列にする。
1. 大科目の値を選んだら、選んだ大科目のインデックスをindex変数に格納する。
1. 小科目のメニューを空にする
1. 大科目のインデックスの値を使って該当する小科目のリストをref変数に格納する。
1. ref変数の要素の数だけforループを回して、「ID："account2」のメニューに小科目を追加していく。

これで、以下のような連動するメニューができると思います。

![displayAnimation.gif](https://qiita-image-store.s3.amazonaws.com/0/136890/1684d107-da1a-2cb9-5a07-1f58f42e14fa.gif "displayAnimation.gif")

メニューの項目を全てJavaScriptで処理しますので、メニューの項目を増減させる場合でも、HTMLファイルは編集する必要がありません。そのため、メニューの項目をHTMLファイルの`<option>`タグに記載してJavaScriptで加工する方法よりも、後々のメンテナンスが楽になるんじゃないかと思います。

***
蛇足
上記のJavaScriptをCoffeeScriptで書いた場合のコードも掲載します。

```coffeescript:index.coffee
accounts1 = [
  "科目を選択",
  "食費",
  "光熱費",
  "医療費",
  "娯楽費",
  "教育費",
  "その他",
]
j = 0
for Acc1 in accounts1
  $('#account1').append("<option value=#{j}>#{Acc1}</value>")
  j += 1

$('#account1').on 'change', ->
  accounts2 = [
    ["科目を選択"]
    ["生鮮食品","中食","外食","飲料","お菓子","加工品",]
    ["電気代","ガス代","水道代","灯油代",]
    ["治療費","処方箋薬","市販薬",]
    ["パチンコ代","旅行代","お土産代","漫画",]
    ["授業料","給食費","塾代","教科書代","書籍代",]
    ["冠婚葬祭費","タクシー代","家賃","税金","仕送り",]
  ]
  index = $('#account1').val()
  $('#account2').attr disabled: false
  $('#account2').empty()
  for Acc2 in accounts2[index]
    $('#account2').append("<option>#{Acc2}</option>")
  @
```