今月のカレンダーであれば前回までで完成です。ここからは、万年カレンダーとして使えるように改良を続けて行きます。
万年カレンダーとは、何年の何月か指定して、任意表示ができるカレンダーのことです。
ここまでの行程で、どんな年月のカレンダーでも表示できるように仕組みは完成しています。自動的に今月の年月を選びますが、来月、再来月といつまでもその月のカレンダーを表示することができます。
あとは、その年月を自由に変更することができる仕組みを取り付ければ良いのです。
利用者が自由にデータを入力するにはフォームが必要になります。
フォームを使って、年と月を入力してもらうようにして、それに応じて任意のカレンダーを表示させれば、万年カレンダーとして活用できます。
入力方式にも色々ありますので、それぞれの利点や使いやすさを考えて決めます。
テキスト入力欄を用意して、自由に数字を入力してもらう方法や、セレクトメニューを使って選んでもらう方法、ボタンを押して数字を増減させる方法などが考えられます。
数字を入力してもらう場合、自由度は高まりますが、入力ミスに対してどういう処置をするか考えなければなりません。
ボタンを押して増減させる方法だと、入力ミスを防ぐことができ、カレンダーをめくっていく感覚にも近くなりますが、大きく年月を変更したいときに手間取ります。
選択方式は、見た目に邪魔なものが少なく、メニューを開いたときに大きく飛ぶこともできて、最も便利な気がします。
月については、1~12までしかないので、選択方式の使い勝手は十分に良いでしょう。
年については、選択肢が多くなると使いにくいので、今年を基準にして前後100年を選択できるようにしようかと思います。
タイトルを「万年カレンダー」に変更します。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>万年カレンダー</title> </head> <body> <h3>万年カレンダー</h3> <hr> <script type="text/javascript"> //日付と時間の設定 now = new Date(); year = now.getFullYear(); mon = now.getMonth()+1; day = now.getDate(); you = now.getDay(); //曜日の選択肢 youbi = new Array("日","月","火","水","木","金","土"); youbi_color = new Array("ff0000","","","","","","0000ff"); //表示の設定 today = year + "年" + mon + "月" + day + "日" + "(<font color='" + youbi_color[you] + "'>" + youbi[you] + "</font>)"; //画面に表示 //document.write(today); //document.write("<hr>"); view_cal(year,mon); function view_cal(cy,cm) { //1日 fday = new Date(cy+"/"+cm+"/1"); fyou = fday.getDay(); //末日 lday = new Array(31,28,31,30,31,30,31,31,30,31,30,31); if ((cy%4 == 0 && cy%100 != 0) || (cy%400 == 0)) {lday[1]++;} //カレンダー表示 document.write("<table border='2'>"); document.write("<tr><th colspan='7'>"+cy+"年"+cm+"月</th></tr>"); document.write("<tr>"); for (m = 0; m < 7; m++){ document.write("<th><font color='" + youbi_color[m] + "'>" + youbi[m] + "</font></th>"); } document.write("</tr>"); for (n = 0; n < 6; n++) { document.write("<tr>"); for (m = 0; m < 7; m++){ d = (n*7+m+1-fyou); if (day == d) {document.write("<td align='right' bgcolor='#ffaaaa'>");} else {document.write("<td align='right'>");} if (d > 0 && d <= lday[cm-1]) {document.write("<font color='" + youbi_color[m] + "'>" + d + "</font>");} else {document.write(" ");} document.write("</td>"); } document.write("</tr>"); if (d >= lday[cm-1]) break; } document.write("</table>"); } </script> </body> </html>
カレンダーの表示部分をユーザー関数にして、任意の年と月を変数cy、変数cmで受け取って処理するように変更します。
それに合わせて、初期設定の最後で、ユーザー関数view_cal()を呼び出しています。このとき、変数yearと変数monを引き渡しています。
これまで直接document.write()を使ってテーブルを書き出していましたが、書き換えをするため一旦変数にテーブルを書き出してから指定場所に出力するように変更します。
それにより、表示の上書きが可能になります。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>万年カレンダー</title> </head> <body> <h3>万年カレンダー</h3> <hr> <div id="calendar"></div> <script type="text/javascript"> //日付と時間の設定 now = new Date(); year = now.getFullYear(); mon = now.getMonth()+1; day = now.getDate(); you = now.getDay(); //曜日の選択肢 youbi = new Array("日","月","火","水","木","金","土"); youbi_color = new Array("ff0000","","","","","","0000ff"); //表示の設定 today = year + "年" + mon + "月" + day + "日" + "(<font color='" + youbi_color[you] + "'>" + youbi[you] + "</font>)"; //画面に表示 //document.write(today); //document.write("<hr>"); view_cal(year,mon); function view_cal(cy,cm) { get_cal = ""; //1日 fday = new Date(cy+"/"+cm+"/1"); fyou = fday.getDay(); //末日 lday = new Array(31,28,31,30,31,30,31,31,30,31,30,31); if ((cy%4 == 0 && cy%100 != 0) || (cy%400 == 0)) {lday[1]++;} //カレンダー表示 get_cal += "<table border='2'>"; get_cal += "<tr><th colspan='7'>"+cy+"年"+cm+"月</th></tr>"; get_cal += "<tr>"; for (m = 0; m < 7; m++){ get_cal += "<th><font color='" + youbi_color[m] + "'>" + youbi[m] + "</font></th>"; } get_cal += "</tr>"; for (n = 0; n < 6; n++) { get_cal += "<tr>"; for (m = 0; m < 7; m++){ d = (n*7+m+1-fyou); if (day == d) {get_cal += "<td align='right' bgcolor='#ffaaaa'>";} else {get_cal += "<td align='right'>";} if (d > 0 && d <= lday[cm-1]) {get_cal += "<font color='" + youbi_color[m] + "'>" + d + "</font>";} else {get_cal += " ";} get_cal += "</td>"; } get_cal += "</tr>"; if (d >= lday[cm-1]) break; } get_cal += "</table>"; //出力 document.getElementById("calendar").innerHTML = get_cal; } </script> </body> </html>
まずは、<div>タグを使ってカレンダーの表示場所をID名「calendar」で用意します。
これによって、ページの再読込をせずにカレンダーを書き換えることができます。
<div>の代わりに<span>を使うことも出来ます。
<div>はその行を全部使いますが、<span>は行内に置いて前後に文字列を書くことができます。
ユーザー関数view_cal()の最初に、変数get_calを空にして用意します。
ユーザー関数内にある「document.write()」は全てこの変数への追加に変更します。
document.write("<table border='2'>");
上記の青字部分を削除し、下記のように修正します。
get_cal += "<table border='2'>";
13箇所ありますので、終わりの「)」の削除も忘れないようにしてください。
カレンダーの上に年月選択用のフォームを作ります。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>万年カレンダー</title> </head> <body> <h3>万年カレンダー</h3> <hr> <form name="fym"> <select name="syear"></select>年 <select name="smon"></select>月 </form> <div id="calendar"></div>
まずは空の状態で入力フォームを用意しておきます。
年と月の選択肢には<select>タグを使いますが、その中に<option>タグを使って年月の数字を用意します。
<option>タグについては、スクリプトによって作成することにします。
特に、年は前後の100年に対応させるため、201行必要になりますし、1つ1つタグを書くのは面倒です。また、今年の年数を基準に範囲も変化するためスクリプトで対応します。
フォームのセレクトメニューで使う<option>タグの記述をスクリプトで作りましょう。
setOption(); view_cal(year,mon); function setOption() { var n; //年 document.fym.syear.length = 201; for (n=0;n<201;n++) { document.fym.syear[n].text = n + year - 100; document.fym.syear[n].value = n + year - 100; } document.fym.syear[100].selected = true; //月 document.fym.smon.length = 12; for (n=0;n<12;n++) { document.fym.smon[n].text = n + 1; document.fym.smon[n].value = n + 1; } document.fym.smon[mon-1].selected = true; }
まずは、ユーザー関数view_cal()を呼び出す前に新たに作るユーザー関数setOption()の呼びだしを用意します。
次にユーザー関数setOption()を作りましょう。
まずは変数nをローカル宣言します。
そして、年のタグから作ります。ここでは、フォーム(fym)とセレクトメニュー(syear)の名前を指定して処理を行います。
まずは、lengthプロパティでメニューの数を201個に設定します。
次にfor文を使って201個のメニューそれぞれに表示の文字(textプロパティ)と選択時の値(valueプロパティ)を入れて行きます。代入している値は今の年数の100年前から100年後までの201年分です。
繰り返しが終わったらメニューができあがっているので、selectedプロパティを使って100番目のメニューを選択します。
月も同様の手順になっています。
メニューの準備ができたら、次はそのメニュー操作をしたときの処理を作りましょう。
フォームから年月を切り替えるとカレンダーも切り替わるようにします。
<form name="fym">
<select name="syear" onChange="change_cal()"></select>年
<select name="smon" onChange="change_cal()"></select>月
</form> (省略) function change_cal() { cy = document.fym.syear.value; cm = document.fym.smon.value; view_cal(cy,cm); }
まず、フォームの<select>タグにイベントを追加します。メニューを切り替えたときに「onChange」が実行され、ユーザー関数change_calを呼び出します。
これにより、年月を切り替える度にカレンダーが切り替わるようになります。
ユーザー関数change_calをスクリプトの中に作りましょう。
ここではカレンダーを表示するユーザー関数へ引き渡す変数を用意します。
まず、変数cyにセレクトメニューの年の値を代入します。
続けて、変数cmにセレクトメニューの月の値を代入しています。
最後に、ユーザー関数view_cal()に2つの変数を引き渡して呼び出しています。
今日の日付に背景色を着けていましたが、このままでは毎月同じ月に色が着いてしまいますので、修正を行います。
function view_cal(cy,cm) { get_cal = ""; //1日 fday = new Date(cy+"/"+cm+"/1"); fyou = fday.getDay(); //末日 lday = new Array(31,28,31,30,31,30,31,31,30,31,30,31); if ((cy%4 == 0 && cy%100 != 0) || (cy%400 == 0)) {lday[1]++;} //カレンダー表示 get_cal += "<table border='2'>"; get_cal += "<tr><th colspan='7'>"+cy+"年"+cm+"月</th></tr>"; get_cal += "<tr>"; for (m = 0; m < 7; m++){ get_cal += "<th><font color='" + youbi_color[m] + "'>" + youbi[m] + "</font></th>"; } get_cal += "</tr>"; for (n = 0; n < 6; n++) { get_cal += "<tr>"; for (m = 0; m < 7; m++){ d = (n*7+m+1-fyou); if (year == cy && mon == cm && day == d) {get_cal += "<td align='right' bgcolor='#ffaaaa'>";}
else {get_cal += "<td align='right'>";} if (d > 0 && d <= lday[cm-1]) {get_cal += "<font color='" + youbi_color[m] + "'>" + d + "</font>";} else {get_cal += " ";} get_cal += "</td>"; } get_cal += "</tr>"; if (d >= lday[cm-1]) break; } get_cal += "</table>"; //出力 document.getElementById("calendar").innerHTML = get_cal; }
背景に色を着けるためにif文で「day == d」と日にちだけを条件にして判断していました。万年カレンダーでは、処理する年月が変わるため、判断条件を増やします。
if文の条件に「year == cy」と「mon == cm」を追加し、それぞれを「&&」でつなぎます。3つの条件が全て満たされたときだけ真となります。
これで万年カレンダーとして、自由に年月を選べるようになりました。
ここからは使いやすいように工夫をして行きたいと思います。
セレクトメニューを使っていくと、マウスを使って翌月のカレンダーを見たいとき、メニューを開いて、選択してと二度のクリックが必要です。また、2つのカレンダーを行き来するのも面倒になります。
そこで、フォームの左右に前月と翌月の切替ボタンを取り付けたいと思います。
<form name="fym"> <input type="button" value="←" onClick="change_month(-1)"> <select name="syear" onChange="change_cal()"></select>年 <select name="smon" onChange="change_cal()"></select>月 <input type="button" value="→" onClick="change_month(1)"> </form>
「←」ボタンを押すと前月、「→」のボタンを押すと次月に年月が進みます。onClickで、ユーザー関数change_month()を呼び出しています。
前月次月を「-1」「1」と数値で表記していますが、これらが何ヶ月であっても対応できるようにスクリプトの方は作っていきます。
カレンダーを切り替えるスクリプトを作ります。
function change_month(ch) { cy = document.fym.syear.selectedIndex; cm = document.fym.smon.selectedIndex; cy = cy + Math.floor(ch/12); cm = cm + ch % 12; document.fym.syear[cy].selected = true; document.fym.smon[cm].selected = true; }
変数chに送られてくる数値が入ります。これは何ヶ月分カレンダーを進めるかという数値です。
マイナスの場合は戻しますが、今は考えないことにします。
まず、最初の2行でフォームから変数cyと変数cmにセレクトタグの項目を取得しています。ここでは「selectedIndex」プロパティを読み取ります。
次の2行で年と月の移動分を計算しています。
年数は、変数chを12で割った後、小数を切り捨てています。これで12以上のときに年数を数えることができます。
月数は、変数chを12で割った余りを算出しています。これで12ヶ月単位の年数部分を除外しています。
そして、最後の2行で、変数cyと変数cmに合わせてセレクトメニューを切り替えています。
しかし、このままでは変数cmが12以上のときに正しく動作をしません。
年と月の修正が必要になります。
function change_month(ch) { cy = document.fym.syear.selectedIndex; cm = document.fym.smon.selectedIndex; cy = cy + Math.floor(ch/12); cm = cm + ch % 12; if (cm > 11) {cy += 1; cm -= 12;} document.fym.syear[cy].selected = true; document.fym.smon[cm].selected = true; cy = document.fym.syear.value; view_cal(cy,cm+1); }
12月にひと月足すと13月になってしまい、このままではエラーが発生します。
そこで、5行目に変数cmを修正するためのif文を入れます。実行の条件は変数cmが11より大きいときになっています。変数cmでは11のときに12月を表しています。
修正する内容は、変数cyに1を足し、変数cmから12を引いています。
どうして「cm = 0」ではなく「cm -= 12」なのか。
変数cmには22までの値が入る可能性があるので、11を超えたら0に戻すというのではなく、12を引く必要があるのです。
最後の2行でもう一工夫します。
変数cyをその前の設定し直したセレクトメニューから取り直しています。今度はプロパティが「value」になっています。「selectedIndex」ではメニューの順番でしたが、今回はメニューの表示と同じ値です。これが年数そのものになります。
変数cmは読み直しをせず、「+1」したものを次で使っています。値は同じになります。
最後にユーザー変数view_cal()を呼び出して、年月を新たに設定したカレンダーが出力されます。
これで変数chが正(プラス)の数の時の対応ができました。
次に変数chがマイナスの時の動作を作ります。
function change_month(ch) { cy = document.fym.syear.selectedIndex; cm = document.fym.smon.selectedIndex; if (ch > 0) { cy = cy + Math.floor(ch/12); cm = cm + ch % 12; } else { ch = Math.abs(ch); cy = cy - Math.floor(ch/12); cm = cm - ch % 12; } if (cm > 11) {cy += 1; cm -= 12;} if (cm < 0) {cy -= 1; cm += 12;} document.fym.syear[cy].selected = true; document.fym.smon[cm].selected = true; cy = document.fym.syear.value; view_cal(cy,cm+1); }
最初はこのままで「Math.floor(ch/12)」も計算できると思っていたのですが、どうしても余計な「-1」が出てしまい、前月のボタンが正しく動作しないことが判ります。
両方を1つのプログラムでできない場合は、分岐を作って別々に作ります。計算を考え直しても良いのですが、時間を掛けずに分岐処理を作ってしまいましょう。。
if文を使って、変数chが正のときと負の時で動作を分けることにします。
if文で変数cyと変数cmへの加算を分岐します。「ch > 0」が正の数、それ以外は負の数として扱っています。「0」は負の数ではありませんが、どちらにしても答えは「0」なのでここでは考えないことにします。プログラムの運用上変数chは0になりません。
負の数の場合、切り捨てのための「Math.floor()」が正しく動作しないので、「Math.abs()」を使って変数chの絶対値を使います。絶対値というのはマイナスをプラスに替えます。
あとは、元の式の「+」を「-」に替えた式を置きます。
分岐の後の変数cmの修正をするif文も2つになります。今度は「cm < 0」の場合です。前の月ということは、1月の前は12月です。一つ前の年になるので、変数cmから1を引き、月数に12を足しています。
動作確認をすると「これで完成!」と思えますが、もう1つ大事なことが残っています。
月数と同様に年数にも上限と下限があるので、これに対応する必要があります。
function change_month(ch) {
cy = document.fym.syear.selectedIndex;
cm = document.fym.smon.selectedIndex;
if (ch > 0) {
cy = cy + Math.floor(ch/12);
cm = cm + ch % 12;
} else {
ch = Math.abs(ch);
cy = cy - Math.floor(ch/12);
cm = cm - ch % 12;
}
if (cm > 11) {cy += 1; cm -= 12;}
if (cm < 0) {cy -= 1; cm += 12;}
if (cy < 0) {cy = 0; cm = 0;}
if (cy > 200) {cy = 200; cm = 11;}
document.fym.syear[cy].selected = true;
document.fym.smon[cm].selected = true;
cy = document.fym.syear.value;
view_cal(cy,cm+1);
}
セレクトメニューは0番から200番まで行がありますので、その範囲内に切り替えしないとエラーが発生します。
変数cyを修正するif文を2つ置きます。
1つ目は、変数cyが0より小さくならないようにします。このとき、年数は0番、月数も0番にしています。
2つ目は、変数cyが200より大きくならないように、年数を200番にして、月数を11番にしています。
年数の範囲を「100」として作ってきましたが、これを後から変更しようとするとあちこちに散らばった数字を手直ししなくてはなりません。
そこで、初期設定に変数h_yearを置いて全体を連動させるようにします。
//年数の範囲 h_year = 100; (省略) function setOption() {
var n;
//年
document.fym.syear.length = h_year * 2 + 1;
for (n=0;n<201;n++) {
document.fym.syear[n].text = n + year - h_year;
document.fym.syear[n].value = n + year - h_year;
}
document.fym.syear[h_year].selected = true;
//月
document.fym.smon.length = 12;
for (n=0;n<12;n++) {
document.fym.smon[n].text = n + 1;
document.fym.smon[n].value = n + 1;
}
document.fym.smon[mon-1].selected = true;
}
function change_month(ch) { cy = document.fym.syear.selectedIndex; cm = document.fym.smon.selectedIndex; if (ch > 0) { cy = cy + Math.floor(ch/12); cm = cm + ch % 12; } else { ch = Math.abs(ch); cy = cy - Math.floor(ch/12); cm = cm - ch % 12; } if (cm > 11) {cy += 1; cm -= 12;} if (cm < 0) {cy -= 1; cm += 12;} if (cy < 0) {cy = 0; cm = 0;} if (cy > h_year*2) {cy = h_year*2; cm = 11;} document.fym.syear[cy].selected = true; document.fym.smon[cm].selected = true; cy = document.fym.syear.value; view_cal(cy,cm+1); }
初期設定でユーザー関数を実行するまでに変数h_yearを置いて、「100」と規定します。
次にユーザー関数setOption()とユーザー関数change_month()の中で、年数の範囲となる部分を変数h_yearを使った式に置き換えます。
ユーザー関数setOption()では4カ所、ユーザー関数change_month()では2カ所です。
何度も触ってみると、いくつか欲しい機能が出てきます。
まずは、「今月」のカレンダーに表示を戻すボタンを作りたいと思います。
<form name="fym"> <table><tr><th> <select name="syear" onChange="change_cal()"></select>年 <select name="smon" onChange="change_cal()"></select>月 </th></tr> <tr><th> <input type="button" value="←" onClick="change_month(-1)"> <input type="button" value="今月" onClick="change_form(year,mon)"> <input type="button" value="→" onClick="change_month(1)"> </th></tr></table> </form>
ボタンが増えるためボタンの位置を年月の下に移動し、テーブルで行を分けることにしましょう。
新しい「今月」のボタンにはonClickで新しく作るユーザー関数change_formを呼び出すようにします。
フォームやカレンダーを今月の表示にするスクリプトを作りましょう。
function change_form(cy,cm) { view_cal(cy,cm); cy = cy - year + h_year; cm = cm - 1; document.fym.syear[cy].selected = true; document.fym.smon[cm].selected = true; }
ユーザー関数change_form()を作りましょう。
まず、変数cyと変数cmにはフォームから今日の年数と月数が送られてきます。ここで直接変数yearと変数monを使わないのは、そのほかのカレンダーにも対応できるようにするためです。
まず、ユーザー関数view_cal()に変数cyと変数cmをそのまま引き継いで呼び出します。これだけでカレンダーは切り替わります。
しかし、フォームのセレクトメニューが連動しないので、その変更が必要になります。
変数cyは年数をメニューの順番に変換し、変数cmは月数を順番に変換しています。
その後、セレクトメニューをselectedプロパティで動かしています。
前後1年分の移動ができるようにボタンを付けましょう。
<form name="fym"> <table><tr><th> <select name="syear" onChange="change_cal()"></select>年 <select name="smon" onChange="change_cal()"></select>月 </th></tr> <tr><th> <input type="button" value="≪" onClick="change_month(-12)"> <input type="button" value="←" onClick="change_month(-1)"> <input type="button" value="今月" onClick="change_form(year,mon)"> <input type="button" value="→" onClick="change_month(1)"> <input type="button" value="≫" onClick="change_month(12)"> </th></tr></table> </form>
ユーザー関数change_month()は月数でカレンダーを切り替えできますので、1年なら12ヶ月の移動で実行できます。
2つのボタンを用意して、それぞれ「-12」「12」をユーザー関数change_month()へ送っています。
これで前後1年の移動が完成です。
カレンダーの表示とフォームのバランスを取るために、カレンダーをテーブル内に入れてみましょう。
(省略) <tr><th> <input type="button" value="≪" onClick="change_month(-12)"> <input type="button" value="←" onClick="change_month(-1)"> <input type="button" value="今月" onClick="change_form(year,mon)"> <input type="button" value="→" onClick="change_month(1)"> <input type="button" value="≫" onClick="change_month(12)"> </th></tr> <tr><th> <div id="calendar"></div> </th></tr></table> </form><div id="calendar"></div>
フォーム内のテーブルに3行目を作り、その中に<div>タグを移動します。
また、メニュー表示に年月があるので、カレンダー内の年月表示をなくしましょう。
(省略) //カレンダー表示 get_cal += "<table border='2'>"; //get_cal += "<tr><th colspan='7'>"+cy+"年"+cm+"月</th></tr>"; get_cal += "<tr>"; (省略)
スクリプトを削除せずに、出力部分の行頭に「//」を付けてコメントアウトします。
万年カレンダーが完成しました。
今日の日付の段階では、パッと表示できるので、カレンダーなんて簡単かなと思ったら、実際には色々と手間が掛かります。
ただのカレンダーでも、箱を並べるだけでも注意点がいくつかあり、更にそこにフォームを使って入力に応じたプログラムが必要でした。
どこでも見かける当たり前の物でも、自分で作るとなると手間が掛かるものです。
まだまだ改良の余地はありますので、色々とチャレンジしてみてください。
・土日の背景にも色を付ける。
・祝日、誕生石、誕生花などの情報を表示
・ブラウザをチェックしてbordercolor(IEのみ)とbgcolorを切り替える
・予定をメモする機能を付ける(データベース機能を付ける)
・印刷用のカレンダーを作る(紙のサイズに合わせた大きさで表示する)
・HPに貼り付けるためにHTMLを出力する(変数get_calの内容をテキストで表示させる)
・1年分のカレンダーを同時に表示する
・12ヶ月カレンダー…1年分のカレンダーを同時に表示(ブラウザチェック付き)
・ロングカレンダー…1月1日~12月31日までを1つのカレンダーとして表示。月ごとに背景色を変更
・六曜カレンダー…旧暦の六曜を表示する万年カレンダー
・月齢カレンダー…月齢に応じた月相を表示する万年カレンダー
・ワイドカレンダー…横長の1年分カレンダー(12カ国語表示)
・月齢ワイドカレンダー…1年分の月齢を表示するカレンダー(6種類表示)