JavaScriptとCSSでカレンダーのドロップダウンリスト(スクロールバー付)を実装する


カレンダーをinput type="month"によるカレンダー選択式ではなくドロップダウンに対応するようにしたかった。当初はselectタグとjavascriptで実装したが、リストにスクロールバーをつけることができないため毎年毎年リストが冗長化してしまいUXに影響を出る可能性があった。そこでドロップダウンにスクロールバーをつけるためjavascriptとCSSで実装した。

目指すもの

・西暦、月をプルダウンで選択できるようにする
・西暦は起点年(画像では2000年)から今年まで入る(毎年自動で年が増える)
・プルダウンリストにスクロールバーをつけ見やすくする

実装に必要な仕様

・ユーザーによって予め選択された年月が入っており、それを初期値とする
・プルダウンメニューにスクロールバーをつけるのにbootstrapを使う

htmlから西暦をhiddenで渡す

/calendar.scala.html
/*プルダウンブロックに関する全html*/
<div class="inmonth" id="yearmonth">
  <!--javascriptにhiddenで年月を渡す-->
  <input type="hidden" id="select-month" value={yearmonth}/>
    <!--bootstrapのdropdownを使う-->
    <div class="dropdown">
      <span class="dm_y" id="dm_y">
        <!--「年」選択のドロップダウンボタン-->
        <button class="btn btn-default dropdown-toggle" type="button" id="menu_y" data-toggle="dropdown" >{year}
        <span class="caret y"></span>
          </button><span> 年 </span>
        <!--ドロップダウンメニューに該当する部分 id設定重要-->
        <ul class="dropdown-menu dmyy" role="menu" aria-labelledby="menu_y" id="id_year"></ul>
        </span>
        <span class="dm_m" id="dm_m">
          <!--「月」選択のドロップダウンボタン-->
          <button class="btn btn-default dropdown-toggle" type="button" id="menu_m" data-toggle="dropdown" >{month}
           <span class="caret m"></span>
           </button><span></span>
      <!--ドロップダウンメニューに該当する部分 id設定重要-->
           <ul class="dropdown-menu dmmm" role="menu" aria-labelledby="menu_m" id="id_month"></ul>
         </span>
    </div>
</div>

html部分では2箇所大事な部分がある
(1)hiddenで初期値を渡すこと
 ┗この初期値に対してjavascriptで処理をするため大切
  ┗本来は”20xx年mm月”という形で入っている
(2)ドロップダウンメニューに該当する部分にそれぞれidを渡すこと
  ┗javascriptではドロップダウンメニューをループで作成する。作成した値を返すのにここで定義したidが必要となる

年月の各プルダウンを作成する処理

calendar.js
/*読み込み時にロードするようにしている*/
window.onload = function selectYearMonth() {
  var optionLoop, pw_m, pw_y, ym, today, this_y, num_ym, str_ym, str_y,str_m,y,m,num_y,num_m;
  today = new Date();
  this_y = today.getFullYear();
  /*hiddenで渡した初期値を以下で年と月に分解し数値化する*/
  ym = $("#select-month").val();
  num_ym = ym.replace(/[^0-9]/g,"");
  str_ym = String(num_ym);
  str_y = str_ym.slice(0,4);
  str_m = str_ym.slice(-2);
  pw_y  = Number(str_y);
  pw_m =  Number(str_m);

  /*初期値をvalueとしてプルダウンに返す*/
  document.getElementById("id_year").value = pw_y;
  document.getElementById("id_month").value = pw_m;

  /*ループ関数定義(スタート数字、終了数字、表示id名、デフォルト数字)*/
  optionLoop = function(start, end, id, pw) {
    var i, opt;
    opt = "<li value='" + start + "'><a href='#'>" + start + "</a></li>";
    for (i = start+1; i <= end ; i++) {
      opt += "<li value='" + i + "'><a href='#'>" + i + "</a></li>";
    }
    return document.getElementById(id).innerHTML = opt;
  }

  /*ループ関数に年月を代入(スタート数字[必須]、終了数字[必須]、表示id名[省略可能]、デフォルト数字[省略可能])*/
  optionLoop(2000, this_y, "id_year", pw_y);
  optionLoop(1,12, "id_month", pw_m);
}

・初期値から漢字を除き、Stringに変換することでsliceを使って年と月に分解できるようにした
・ループ部分は参考にしたURLの処理をほとんど借りた
・最後のreturnで作成したそれぞれのドロップダウンメニューをdocumentgetElementByIDを使ってhtmlに返している

年月をクリックで選択肢、その値をvalueにする処理

click_event.js
/*例として年選択の動作を設定する。月も同様の設定となる*/
$('.dm_y').on( 'click', 'a', function() {
    var text = $(this).html();
    var htmlText = text + ' <span class="caret y"></span>';
    $(this).closest('.dm_y').find('.dropdown-toggle').html(htmlText);
    document.getElementById("id_year").value = text;
});

・ドロップダウンから値を選択し、その値をvalueとして扱うための処理である

プルダウン中のリスト高さをCSSで決める

main.css
/*例として年のプルダウンに関するCSSを提示。月も同様に設定する*/
.dm_y .dropdown-menu{
    /*表示するスクロールバーの高さを調整。今回は月がスクロールなしで入る高さにした*/
    max-height: 330px;
    /*スクロールバーを表示する*/
    overflow: scroll;
    /*デフォルトで水平方向のスクロールバーが入ってくるので非表示にする*/
    overflow-x: hidden;
    margin-top:0px;
}

おまけ

connect_year_month.js
    var month = Number(new_y) + "-" + ("00" + new_m).slice(-2);

このあとは選択したvalueで保存するボタンなどを押すことで選択年月のデータを表示する仕組みなのだが、その前の処理として別れている年月を結合する必要がある。
仕様上、"20xx-mm”という形で年月を結合するが、プルダウンでは月を数値化しているため1~9月までは一桁となってしまいこのままではうまく値を抽出できない。そこで接頭に0をつけるやり方があったのでそれを実装した。

参考文献

簡単にHTMLの年月日のプルダウンを作る方法
HTML選択ボックスの高さ(ドロップダウン)
数字の先頭を0埋めする方法