原生JS實現日曆

這周寫本身的項目發現又用到日曆了,加之本身畢業以後的第一個工做中遇到的任務也是須要寫個日曆(組員寫了,我就不用寫了)

今天就來好好折騰一下日曆是怎麼寫的。html

首先,咱們看看 windows 的日曆。發現總共有這麼幾個元素。先實現試試。

  • 1.年份的選擇、月份的選擇
  • 2.週一 ~ 週日(週日 ~ 週六)
  • 3.日曆格子 6*7 = 42

從數據的角度來分析日曆的實現是比較簡單的

  • 1.咱們須要顯示一個當前時間的結構 - new Date()
  • 2.咱們須要顯示當月的信息 - [星期(週一~週日),日期(1-[28,29,30,31])]
    • 其中咱們只要知道了每月的 1日 是星期幾,就能很容易地擺放後面的日子(萬事開頭難)。
    • 咱們最多須要 6 行來顯示咱們的日期,由於要考慮週一是星期日的狀況 1+7*4 = 29(五行)顯然是不夠的
    • 肯定了 6 行以後,咱們發現咱們可能須要獲取上個月,和下個月多出來的幾天的擺放位置。
    • 不一樣年份的不一樣月的 2月份,咱們知道它的日期是不一樣的,因此咱們還須要判斷 平年仍是閏年。
  • 3.顯示上個月,下個月的切換。咱們發現須要有個函數來幫咱們更新日曆。windows

    這裏咱們就要考慮一下,到底要怎麼更新這些 dom 裏面的數據了,從新刪除插入 dom 確定是不太好的。數組

分析完以後,讓咱們跟着 新增/修改 一些代碼。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
<style>
  .week-item {
    display: inline-block;
    width: 80px;
    height: 40px;
    line-height: 40px;
    border: 1px solid sandybrown;
    text-align: center;
  }
  .date-item {
    display: inline-block;
    width: 80px;
    height: 40px;
    line-height: 40px;
    border: 1px solid beige;
    text-align: center;
  }
</style>
</head>
<body>
  <div class="wrapper">
    <div class="year-line">
      <button id="preMonth" class="year-prev">上一月</button>
      <button id="nowYear" class="year-now"></button>
      <button id="nowMonth"></button>
      <button id="nowDate"></button>
      <button id="nextMonth" class="year-next">下一月</button>
    </div>
    <div id="weekLine" class="week-line"></div>
    <div id="dateWrap" class="date-wrap"></div>
  </div>
</body>
<script>
  // 工具方法 - start
  // 1.爲了得到每月的日期有多少,咱們須要判斷 平年閏年[四年一閏,百年不閏,四百年再閏]
  const isLeapYear = (year) => {
    return (year % 400 === 0) || (year % 100 !== 0 && year % 4 === 0);
  };
  // 2.得到每月的日期有多少,注意 month - [0-11]
  const getMonthCount = (year, month) => {
    let arr = [
      31, null, 31, 30, 
      31, 30, 31, 31,
      30, 31, 30, 31
    ];
    let count = arr[month] || (isLeapYear(year) ? 29 : 28);
    return Array.from(new Array(count), (item, value) => value + 1);
  };
  // 3.得到某年某月的 1號 是星期幾,這裏要注意的是 JS 的 API-getDay() 是從 [日-六](0-6),返回 number
  const getWeekday = (year, month) => {
    let date = new Date(year, month, 1);
    return date.getDay();
  };
  // 4.得到上個月的天數
  const getPreMonthCount = (year, month) => {
    if (month === 0) {
      return getMonthCount(year - 1, 11);
    } else {
      return getMonthCount(year, month - 1);
    }
  };
  // 5.得到下個月的天數
  const getNextMonthCount = (year, month) => {
    if (month === 11) {
      return getMonthCount(year + 1, 0);
    } else {
      return getMonthCount(year, month + 1);
    }
  };
  // 工具方法 - end
  let weekStr = '日一二三四五六';
  weekArr = weekStr.split('').map(item => '星期' + item);
  // 插入星期 dom
  let weekDomStr = '';
  let oFragWeek = document.createDocumentFragment();
  weekArr.forEach(item => {
    let oSpan = document.createElement('span');
    let oText = document.createTextNode(item);
    oSpan.appendChild(oText);
    oSpan.classList.add('week-item');
    oFragWeek.appendChild(oSpan);
  });
  let weekWrap = document.getElementById('weekLine');
  weekWrap.appendChild(oFragWeek);

  // 這裏得到咱們第一次的 數據 數組
  const updateCalendar = (year, month, day) => {
    if (typeof year === 'undefined' && typeof month === 'undefined' && typeof day === 'undefined') {
      let nowDate = new Date();
      year = nowDate.getFullYear();
      month = nowDate.getMonth();
      day = nowDate.getDate();
    }
    // 更新一下頂部的年月顯示
    document.getElementById('nowYear').innerHTML = year;
    document.getElementById('nowMonth').innerHTML = month + 1;
    document.getElementById('nowDate').innerHTML = day;
    // 生成日曆數據,上個月的 x 天 + 當月的 [28,29,30,31]天 + 下個月的 y 天 = 42
    let res = [];
    let currentMonth = getMonthCount(year, month);
    let preMonth = getPreMonthCount(year, month);
    let nextMonth = getNextMonthCount(year, month);
    let whereMonday = getWeekday(year, month);
    let preArr = preMonth.slice(-1 * whereMonday);
    let nextArr = nextMonth.slice(0, 42 - currentMonth.length - whereMonday);
    res = [].concat(preArr, currentMonth, nextArr);
    // 上面通過我本人的測試是沒有什麼問題,接下來就是更新 dom 的信息的問題
    let hadDom = document.getElementsByClassName('date-item');
    if (hadDom && hadDom.length) {
      let domArr = document.getElementsByClassName('date-item');
      for (let i = 0; i < domArr.length; i++) {
        domArr[i].innerHTML = res.shift();
      }
    } else {
      // 若是以前沒有結構的話
      let str = '';
      for (let i = 0; i < 6; i++) {
        str += '<div class="date-line">';
        for (let j = 0; j < 7; j++) {
          str += `<span class='date-item'>${res.shift()}</span>`;
          if (j === 6) {
            str += '</div>';
          }
        }
      }
      document.getElementById('dateWrap').innerHTML = str;
    }
  };

  updateCalendar();
  // 添加上一月,下一月事件
  let oPreButton = document.getElementById('preMonth');
  let oNextButton = document.getElementById('nextMonth');
  oPreButton.addEventListener('click', function () {
    let currentYear = +document.getElementById('nowYear').textContent;
    let currentMonth = +document.getElementById('nowMonth').textContent - 1;
    let currentDate = +document.getElementById('nowDate').textContent;
    if (currentMonth === 0) {
      updateCalendar(currentYear - 1, 11, currentDate);
    } else {
      updateCalendar(currentYear, currentMonth - 1, currentDate);
    }
  });
  oNextButton.addEventListener('click', function () {
    let currentYear = +document.getElementById('nowYear').textContent;
    let currentMonth = +document.getElementById('nowMonth').textContent - 1;
    let currentDate = +document.getElementById('nowDate').textContent;
    if (currentMonth === 11) {
      updateCalendar(currentYear + 1, 0, currentDate);
    } else {
      updateCalendar(currentYear, currentMonth + 1, currentDate);
    }
  });
</script>
</html>

發現用 dom 直接操做而不是經過 mvvm 框架實現確實仍是比較蛋疼的,如下是此次實現的效果。

實現一個功能的時候,從數據的層面分析,有時候會比較容易理解app

相關文章
相關標籤/搜索