こんにちは。今回も引き続き、
Dateオブジェクト
カレンダーを実装する前に、
まず、
- 引数なし→現在の日時
- 文字列→日付を表す文字列としてパースして解釈
- 数値(1つ)→1970年1月1日00:00:00時点からのミリ秒数として解釈
- 数値(2つ以上)→第一引数から順番に、
年、 月、 日、 時、 分、 秒、 ミリ秒として解釈
new Date(); // 現在日時
new Date('2011/2/28');
new Date(1298818800000);
new Date(2011, 1, 28);
このように、
このDateオブジェクトはgetDate, setDateといった、
なお、
また、
ある月の最終日
カレンダーを書く上で、
といっても、
つまり、 new Date(year, month, 0) とすればその月の晦日が取得できます。なお、
カレンダーを書く
さてカレンダーを書く際にもう一つ考えなければいけないのが、
行数は当然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 ? ' ' : date++;
tr.appendChild(td);
}
tbody.appendChild(tr);
}
空白のセルには をいれています。これは空白のセルが潰れてしまうことを防ぐための応急処置です。
カレンダーの実装
ここまででカレンダーはほぼ出来上がりなので、
見た目についてはもちろん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%;
}
さて汎用的な実装の部分ですが、
(function(){
var day_ja = ['日', '月', '火', '水', '木', '金', '土'];
// 外から見えるメソッドを定義
function JCalendar(parent){
if (typeof parent === 'string') {
parent = document.getElementById(parent);
}
this.parent = parent;
}
window.JCalendar = JCalendar;
JCalendar.prototype = {
create: create,
update: update,
remove: remove,
set_caption: set_caption,
set_body: set_body,
set_date: set_date,
onclick_date: onclick_date,
onclick_month: onclick_month
};
// 上記のメソッドの中身
function onclick_date(id, year, month, date){
return false;
}
function onclick_month(id, year, month){
this.update(+year, +month);
return false;
}
function remove(){
this.parent.removeChild(this.table);
}
function update(year, month){
this.remove();
this.create(year, month);
}
function set_date(year, month){
var today = new Date();
this.month =parseInt(month, 10)|| (today.getMonth()+1);
this.year = parseInt(year, 10) || today.getFullYear();
}
function set_caption(year, month){
var caption = document.createElement('caption');
var div = document.createElement('div');
var next = document.createElement('a');
next.href = '#month-' + ((month === 11) ? year+1 : year)
+ '-' + (month===11?1:month+1);
next.className = 'next';
next.innerHTML = '→';
var prev = document.createElement('a');
prev.href = '#month-' + ((month === 1) ? year-1 : year)
+ '-' + (month===1?12:month-1);
prev.className = 'prev';
prev.innerHTML = '←';
var current = document.createElement('span');
var text = document.createTextNode(year + '/' + month);
current.appendChild(text);
div.appendChild(prev);
div.appendChild(current);
div.appendChild(next);
caption.appendChild(div);
this.table.appendChild(caption);
}
function set_body(year, month){
var tbody = document.createElement('tbody');
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 date = 1;
var skip = true;
for (var row = 0; row < 7; row++) {
var tr = document.createElement('tr');
for (var col = 0; col < 7; col++){
if (row === 0){
var th = document.createElement('th');
var day = day_ja[col];
th.appendChild(document.createTextNode(day));
th.className = 'calendar day-head day' + col;
tr.appendChild(th);
} else {
if (row === 1 && first_day === col){
skip = false;
}
if (date > last_date) {
skip = true;
}
var td = document.createElement('td');
td.className = 'calendar day' + col;
if (!skip) {
var a = document.createElement('a');
a.href = '#day-' +year+ '-' +month+ '-' +date;
a.appendChild(document.createTextNode(date));
td.appendChild(a);
date++;
} else {
td.innerHTML='<span class="blank"> </span>';
}
tr.appendChild(td);
}
}
tbody.appendChild(tr);
}
this.table.appendChild(tbody);
}
function create(year, month){
var that = this;
var table = document.createElement('table');
table.className = 'js_calendar';
this.table = table;
table.onclick = function(e){
var evt = e || window.event;
var target = evt.target || evt.srcElement;
if (target.tagName === 'A' &&
target.hash.indexOf('#day-') === 0) {
return that.onclick_date.apply(that,
target.hash.match(/day-(\d+)-(\d+)-(\d+)/));
} else if (target.tagName === 'A' &&
target.hash.indexOf('#month-') === 0) {
return that.onclick_month.apply(that,
target.hash.match(/month-(\d+)-(\d+)/));
}
};
this.set_date(year, month);
this.set_caption(this.year, this.month);
this.set_body(this.year, this.month);
this.parent.appendChild(table);
}
})();
new JCalendar('j-calendar2').create();
このJCalendarはnewした後のオブジェクトについてメソッドを上書きすることで挙動を一部だけ変えることができます。例えば、
(function(){
var mycal = new JCalendar('j-calendar3');
var input = document.getElementById('j-calendar3-i');
mycal.create();
mycal.onclick_date = function(id,year,month,date){
input.value = [year,month,date].join('/');
return false;
};
})();
まとめ
今回はJavaScriptを使ったUIとしてカレンダーの実装を紹介しました。こちらもまだまだ改良の余地があるので、
次回はこれまで作ったパーツの幾つかを組み合わせたアプリを作ってみたいと思います。