これでできる! クロスブラウザJavaScript入門

第24回 JavaScriptによるUIの実装:カレンダー

この記事を読むのに必要な時間:およそ 5 分

こんにちは。今回も引き続き,JavaScriptでUIを実装する方法を紹介していきます。今回はやや趣向を変えて,JavaScriptでカレンダーを実装してみます。

Dateオブジェクト

カレンダーを実装する前に,JavaScriptのDateオブジェクトについておさらいしておきましょう。

まず,Dateオブジェクトはnew演算子でインスタンスを作ってから使います。そのとき渡す引数は4つのタイプがあります。

  • 引数なし→現在の日時
  • 文字列→日付を表す文字列としてパースして解釈
  • 数値(1つ)→1970年1月1日00:00:00時点からのミリ秒数として解釈
  • 数値(2つ以上)→第一引数から順番に,年,月,日,時,分,秒,ミリ秒として解釈

Dateオブジェクトの初期化

new Date(); // 現在日時
new Date('2011/2/28');
new Date(1298818800000);
new Date(2011, 1, 28);

このように,何か引数を渡すとそれを日時として解釈して,その日時を基点とするDateオブジェクトを作ります。

このDateオブジェクトはgetDate, setDateといった,set/getメソッドをセットで持っており,一度作ったDateオブジェクトの日時を操作することができます。

なお,多くのブラウザはdate.getYearとdate.getFullYearというメソッドを実装しており混同されがちです。実はgetYearのほうはECMAScriptで定義されていない非標準なメソッド(正確には,ECMA 262 5th editionの付録としてその仕様について言及があります)で,IE 6~8以外のブラウザは1900年から数えた年数(2011年なら111)を返すので,getYearは使うべきでないメソッドのひとつです。

また,getFullYear,getMonth,getDateのうち,getMonthだけは0オリジンであることにも注意が必要です。ちなみに,C/Java/Perlなども月は0オリジンです。英語圏では月をJan,Feb,Marなどのように文字で表記することが多いため,0オリジンのほうが配列との相性がよかったのだと思われます。MonthのほかにDay(曜日)も0オリジンです。曜日を表示するのには,0オリジンのほうが都合がよいことが実感できると思います。

ある月の最終日

カレンダーを書く上で,一日と晦日を取得する必要があります。一日は new Date(year, month-1, 1) でよいので簡単ですね(monthにさえ注意すれば!)⁠問題は晦日です。

といっても,実はちゃんとその方法が用意されていて,日にちに0(もしくはマイナスの値)を渡すとDateオブジェクトは前月に遡ってくれます。

つまり, new Date(year, month, 0) とすればその月の晦日が取得できます。なお,月についても考慮してくれるので, 2010年の大晦日を取得したい時は, new Date(2010, 12, 0) で取得できます(わざわざ,new Date(2011, 1, 0) のようにしなくても大丈夫です)。

カレンダーを書く

さてカレンダーを書く際にもう一つ考えなければいけないのが,何列書くか,という問題です。といっても,カレンダーは最短で4週(2月1日が日曜で,うるう年でない場合のみ)⁠最長で6週の3つのパターンしかないので,6列で決め打ちにしてしまってもよいでしょう。

行数は当然7行ですから,6回ループする中に7回のループを入れ子にするという処理は見えてきました。あとは,どこから一日を始めて,晦日で終了する条件だけですね。

まず,一日は曜日によってどの位置になるかが決定します(曜日によって曜日が決まると言っているようなもので,当たり前のことなのですが)⁠晦日は加算していった日にちが晦日と一致するところで判断できます。これらの間ではそれぞれの日にちを,それ以外では空白のセルで埋めればシンプルなカレンダーの出来上がりです。

カレンダーの骨組み

var first = new Date(year, month-1, 1);
var last  = new Date(year, month  , 0);
var first_day = first.getDay();// 一日の曜日
var last_date = last.getDate();// 晦日の日にち
var skip = true; // 日にちを埋めるかどうかのフラグ
var date = 1;
for (var row = 0; row < 6; row++){
  var tr = document.createElement('tr');
  for (var col = 0; col < 7; col++) {
    if (row === 0 && first_day === col){
      skip = false;
    }
    if (date > last_date) {
      skip = true;
    }
    var td = document.createElement('td');
    td.innerHTML = skip ? '&nbsp;' : date++;
    tr.appendChild(td);
  }
  tbody.appendChild(tr);
}

空白のセルには&nbsp;をいれています。これは空白のセルが潰れてしまうことを防ぐための応急処置です。

カレンダーの実装

ここまででカレンダーはほぼ出来上がりなので,見た目や実装について少し工夫してみましょう。

見た目についてはもちろんCSSで制御します。captionやth要素などを使って,なるべく無駄なく装飾します。

カレンダーの装飾

.js_calendar{
  font-size:80%;
  font-family:"Verdana", sans-serif;
}
.js_calendar td{
  border:1px solid #ddd;
  text-align:right;
  -moz-border-radius:3px;
       border-radius:3px;
}
.js_calendar caption{
  text-align:left;
}
.js_calendar caption div{
  position:relative;
}
.js_calendar caption span{
  display:block;
  padding:3px;
  text-align:center;
}
.js_calendar caption a{
  display:inline-block;
  position:absolute;
  background:#eee;
  text-decoration:none;
  font-weight:bold;
  padding:3px;
  top:0px;
  -moz-border-radius:4px;
       border-radius:4px;
}
.js_calendar caption a:link,
.js_calendar caption a:visited{
  color:#666;
}
.js_calendar caption a:hover{
  color:#f0f;
}
.js_calendar caption a.next{
  right:4px;
}
.js_calendar caption a.prev{
  left:4px;
}
.js_calendar td.calendar a{
  padding:2px;
  display:block;
  background:#eee;
  text-decoration: none;
  color: #333;
}
.js_calendar td.calendar span.blank{
  padding:2px;
  display:block;
}
.js_calendar th.day0{
  color:red;
}
.js_calendar th.day6{
  color:blue;
}
.js_calendar td.day0 a{
  background:#fdd;
}
.js_calendar td.day6 a{
  background:#def;
}
.js_calendar a:hover{
  color:#f0f;
}
.js_calendar th{
  font-weight:normal;
  font-size:90%;
}

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

URLhttp://d.hatena.ne.jp/os0x/

コメント

  • カレンダーの挙動について

    カレンダーの「→」をクリックすると次の月に移りますが、11月の次が12月ではなく翌年の1月になります。

    また、1月のカレンダーを表示した状態から「←」をクリックすると前年の12月を表示しますが、12月を表示したその状態から「→」をクリックしていくと、表示上の年月はその年の13月、14月・・・になります。

    これはfunction set_captionの中のnext.hrefへの代入文にある二箇所のmonth === 11が原因だと思います。
    month === 12としたところ正常に動作しました。

    こちらの連載で勉強させて頂いております。ありがとうございます。これからも連載を楽しみにしております。

    Commented : #1  うちのお父さんは生麺の焼きそばを作るときも水を入れるから麺がブヨブヨ (2011/05/01, 20:46)

コメントの記入