[JavaScript] 產生月曆

這是 JavaScript 的一個作業的一部份,我把他拆成三個部份寫。這是最後一部份,在確定是合理存在的日期後,產生當月的月曆,如下:

顯然這個問題不是只有顯示該月的天數而已,為了讓月曆好看,可能要補足上個月與下個月的部份,這才是這個問題要處理的重點。以上圖來說,2014 年 7 月有 31 天,不只要把星期二的 7 月 1 日到星期四的 7 月 31 日做出來,還要把前後的 6 月 29 日、6 月 30 日、8 月 1 日、8 月 2 日補上,這才是我們常見的月曆。

需判斷的東西

主體是這個月,所以當月共有幾天要先知道,這在上一篇 [JavaScript] 得到某年某月的天數 做出來了,想法是 Date 物件 Month 多 1,Date 取 0 退一天。

第二為了知道需不需要補足上個月的最後幾天,要知道當月第一天是星期幾,這利用 Date 物件的 getDay() 方法(星期日回傳0,星期一回傳1,....星期六回傳6)。

第三,若上個月最後幾天也要做出來,表示上個月共有幾天也要求得,這利用 Date 物件取當月,然後日期用 0 可得上個月最後一天物件。

第四為了知道是否要補齊下個月的頭幾天,就要求出當月最後一天是星期幾,前上一篇有求出當月最後一天的 Date 物件,再 .getDay() 回傳即可。

然後目標是將日期用 Array 依序列出,日期小於 1 的是上個月(例如下圖的 - 1、0),大於當月總天數的是下個月(例如下圖的 32、33)。

如上圖 2014 年 7 月的月曆,就是要產生一個陣列 days = [ -1, 0, 1, 2, 3, ... , 31, 32, 33 ]

function generateCalender(inputYear, inputMonth, inputDate) {

  let domCalendar = document.getElementById('tableCalendar');
  if (domCalendar) {             // 若已產生過日曆就刪掉他,不然會一直累加
    domCalendar.remove();
  }
  let days = [];
  let startDate = new Date(inputYear, inputMonth, 1);       // 當月第一天的 date object
  let endDate = new Date(inputYear, inputMonth + 1, 0);     // 當月最後一天的 date object
  let startDay = startDate.getDay();                        // 當月第一天是禮拜幾
  let endDay = endDate.getDay();                                    // 當月最後一天是禮拜幾
  let lastMonthDate = new Date(inputYear, inputMonth, 0).getDate(); // 上個月總天數
  let thisMonthDate = endDate.getDate();                            // 當月總天數

  for (let i = 0; i < startDay; i++) {          // 若當月第一天不是禮拜日,補齊上個月的天數
    days.push(- startDay + 1 + i);
  }
  for (let i = 1; i <= thisMonthDate; i++) {    // 當月的所有天數
    days.push(i);
  }
  for (let i = 1; i < 7 - endDay; i++) {        // 若當月最後一天不是禮拜六,補齊下個月的天數
    days.push(thisMonthDate + i);
  }

  let nodeTable = document.createElement('table');
  nodeTable.setAttribute('id', 'tableCalendar');

  for (let i = 0; i < days.length; i += 7) {
    let weekRow = nodeTable.insertRow();      // 開始一個 tr
    for (let j = 0; j < 7; j++) {
      let thisDate = days[i + j];
      let weekCell = weekRow.insertCell();    // 即 td

      if (thisDate < 1) {                     // days 存的若是負數表示上個月的日期
        weekCell.textContent = thisDate;
        weekCell.className = 'notThisMonth';
      }
      else if (thisDate > thisMonthDate) {    // 下個月的日期
        weekCell.textContent = thisDate;
        weekCell.className = 'notThisMonth';
      }
      else if (thisDate == inputDate) {       // select 選的當日,就用專屬的 class
        weekCell.textContent = thisDate;
        weekCell.className = 'todayCell';
      }
      else {                                  // 這個月其它天數
        weekCell.textContent = thisDate;
      }
    }
  }
  let thead = nodeTable.createTHead();     // thead 若在前面加,不知道為何 class 會有問題
  let theadRow = thead.insertRow();
  let strThead = ['日', '一', '二', '三', '四', '五', '六'];
  for (let str of strThead) {
  let th = theadRow.insertCell();
  th.textContent = str;
  }
  document.getElementById('divCalendar').appendChild(nodeTable);
}

留言