カレンダを用いたデータ入力サポート

カレンダをデータ入力などのためのインタフェースとして用いることは度々あるためか,様々なライブラリでカレンダーを表示し,入力を助けるための関数が用意されている.だが,それらは決してかゆいところまで届くようにはできていないのと,それらの関数を読んでも,実際にどのように実装しているのかが良く分からないので作ってみることにする.

具体的には,国際会議や研究会・論文投稿の日程を蓄えて,タグ付けするシステムを構築したいと考えており,そのために適したカレンダ表示スクリプトの作成を行なう.

カレンダの作成の準備
カレンダの表示の種類

カレンダの表示はいろいろ考えられるが、とりあえず次のようなタイプが考えられる。

  • 1日のタイムテーブル
  • 週(横)
  • 週(縦)
  • 週(横)+タイムテーブル
  • 2週(横2段)
  • 月(横)
  • 月(縦)
  • 月(横)曜日での段割:標準的なカレンダー表示?
  • 年(横4段)
  • 年(横4段)カレンダータイプ?
  • 年(縦12段)

これらの表示方法に名前があったりするのだろうか?

カレンダの表示(縦)

単純に縦にカレンダの表示をするには, div要素を積み重ねるだけでよい。その中に日付を

カレンダの表示(横 or 段組み)

div 要素は、位置決めが標準である場合、強制的に改行されてしまう。そのため、単純に要素の追加だけでは生成できない。そこで Google カレンダなでどのように表示しているのかを見てみると,

<div style="position:absolute; width:500px;" id="colheaders"> <div style="position:absolute; width: 14.2857%; left: 0%;"><span>日</span></div> <div style="position:absolute; width: 14.2857%; left: 14.2857%;"><span>月</span></div> <div style="position:absolute; width: 14.2857%; left: 28.5714%;"><span>火</span></div> <div style="position:absolute; width: 14.2857%; left: 42.8571%;"><span>水</span></div> <div style="position:absolute; width: 14.2857%; left: 57.1429%;"><span>木</span></div> <div style="position:absolute; width: 14.2857%; left: 71.4286%;"><span>金</span></div> <div style="position:absolute; width: 14.2857%; left: 85.7143%;"><span>土</span></div> </div>


実際に表示させるとこのようになる。ただし、これでは高さが指定されていないので高さの指定方法も考える必要がある。

このあたりの話になると Javascript というよりは HTML とレンダリングエンジンの話になってしまいそうだが,とりあえず,親要素の大きさ(幅)にあわせてカレンダの表示の大きさをきめたいとするときに id = "colheaders" の width を 100% とすると.上と同じように記述した場合 static では,画面の右端を始点とした表示領域全体に, absolute では,配置場所を始点とした表示領域の幅分を幅とした表示がされる.親要素となる div 要素の幅に納めるには relative にすると表示できた.

だが,これは全てのブラウザに共通しているかどうかは確かめていない.

月の最終日

JavaScript の Dateオブジェクトの実装が全て同じかどうか分からないが,最終日を知るには次の月の 0日目を知る方法がある.


  date  = new Date();
  date2 = new Date(date.getYear(), date,getMonth()+1, 0);
   	     

当然,setDate(0)をつかうという方法もあるが,下の方法では先月の最終日が分かるだけなので,間違えないようにする必要がある.


  date  = new Date();
  date.setDate(0);
   	     
プログラム作成

日付グリッドの表示 ver.0.1

テーブルとかを使った方が楽なのかもしれないけど, Drag での範囲指定とかを考えると,おそらくこちらの方が楽だろうから div でグリッドを作成.そのために,表示タイプに応じた表示位置を指定している.縦,横ともに表示幅をパーセンテージで指定しているのだから,100%になるまで積み上げるというのも考えたけど,縦に積み上げるのか横に積み上げるのかで順序が変わることに気が付いて挫折.まぁ,表示タイプを別オプションで指定すればいいだけのことだけど。

とりあえず,グリッド表示メソッド


/**
 * @param tDay: 表示部分に含めなくてはいけない日付
 * @param fdate: 表示部分に含めなくてはいけない日付の曜日
 * @param nDays: 表示する日数(週表示なら7日, 月なら31など)
 * @param cellw,cellh: 一つのセルの表示幅
 */
createCalGrid: function(tDay, fdate, nDays, cellw, cellh, frame, type) {
   / 既存のdaysがある場合の処理も必要
   for(var i = tDay - fdate; i < nDays + (tDay - fdate); i++) {
     var dayData = new Day(this.year,this.month,i);
     var ID = dayData.id;
     dayData.setCellSize(cellw + "%", cellh + "%");
     var leftpos = 0, toppos = 0;
     switch(type) { // グリッド内部の表示位置の指定
     case 'weekH':  // 横
	leftpos = cellw * dayData.day, toppos   = 0, break;
     case 'weekV':  // 縦
	leftpos = 0, toppos  = cellh * dayData.day, break;
     case '2weekH': // 横
	leftpos = cellw * dayData.day;
	toppos = cellh * intVal((i+fdate-tDay)/7), break;
     case '2weekV':
	leftpos = cellw * intVal((i+fdate-tDay)/7);
	toppos = cellh * dayData.day, break;
     case 'monthH':
	leftpos = cellw * (dayData.date - 1), toppos  = 0, break;
     case 'monthV':
	leftpos = 0, toppos  = cellh * (dayData.date - 1), break;
     case 'monthW':
	leftpos = cellw * dayData.day;
	toppos  = cellh * intVal((i+fdate-tDay)/7);
     }
     frame.appendChild(dayData.createCell(leftpos+"%",toppos+"%")); // セルの作成

   }
},	       
	   

カレンダーヘッダの表示 ver.0.0.1

カレンダ表示は,当然日付だけでは済まない.曜日や年月を表示する必要がある.日付表示部分をグリッド部とし,曜日,年月を表示する部分をヘッダ部と考える.ただし,このヘッダ部は,表示方法によって上に表示するもの,横に表示するものが変わってくるので表示指定のタイプを現在の一つだけだと苦しい.とりあえず,上に表示するだけであるなら,次のような表示が考えられる.


 createCalHeader: function(sweek, nDays, cellw, cellh, mheader) {
   for(var i = 0; i < nDays; i++) {
     var hcell = document.createElement('div');
     hcell.style.position = "absolute";
     hcell.style.width  = (cellw*0.99) + "%";
     hcell.style.height = (cellh*0.99) + "%";
     hcell.style.left = (cellw * i) + "%";
     hcell.style.top = 0+"%";
     hcell.style.textAlign = 'center';
     hcell.style.backgroundColor = hbgc;
     hcell.style.border = "1px dashed #bbf"
     var doc = document.createElement('span');
     doc.appendChild(document.createTextNode(this.dayWords[sweek++]));
     doc.style.textAlign="center";
     hcell.appendChild(doc);
     mheader.appendChild(hcell);
     sweek = (sweek > 6)? 0 : sweek;
  }
  console.log(mheader);
},
	     

うーん,もっとよさげな方法はありそう.とりあえず divなどの要素を作成する部分を他のライブラリなどを参考に良い方法を考える必要はありそう.

日数カウンター ver.0.0.1

function getDaysCount(date1, date2) {
    var days;
    if(date1 > date2)
	days = date1 - date2;
    else
	days = date2 - date1;
    return days/(60*60*24*1000)
}
	     

17歳ジェネレータ ver.0.0.1

上の日数計算を使って何となく作ってみたもの.正確かどうかは不明.

 function generate17(date1) {
    year = date1.getFullYear();
    date1.setFullYear(year+17);
    today = new Date();
    today.setHours(0);
    today.setSeconds(0);
    today.setMinutes(0);
    today.setMilliseconds(0);
    return "17歳 ト "+getDaysCount(date1,today)+"日 デス";
    //return date1
}
	     

GoogleChartAPIをつかって棒カレンダー表示

上の二つはこれがやってみたかっただけ.
やっつけで,適当すぎるけどおもしろいことができたらいいなぁ.

function drawBarCalendar(day1, day2, title) {
    var start = new Date(2008,0,1);
    var end = new Date(2008,0,1);
    end.setFullYear(start.getFullYear()+1);
    var yearDays = getDaysCount(start, end);
    var tmp1 = (getDaysCount(start,day1)/yearDays)*100.0;
    if(day2 > end)
	day2 = end
    var tmp2 = (getDaysCount(day1,day2)/yearDays)*100.0;
    var tmp3 = (getDaysCount(day2,end)/yearDays)*100.0;
    var text = "";
    return text
}
	     
問題点があるとしたら,y軸を複数設定したときに,データ点の順番とy軸のラベルの順番が逆になってしまうのはなぜだろう.