zoukankan      html  css  js  c++  java
  • 原生JS实现日历

    已修正网友指正的错误~感谢督促

    这周写自己的项目发现又用到日历了,加之自己毕业之后的第一个工作中遇到的任务也是需要写个日历(组员写了,我就不用写了)

    今天就来好好折腾一下日历是怎么写的。

    首先,我们看看 windows 的日历。发现总共有这么几个元素。先实现试试。

    • 1.年份的选择、月份的选择
    • 2.周一 ~ 周日(周日 ~ 周六)
    • 3.日历格子 6*7 = 42

    从数据的角度来分析日历的实现是比较简单的

    • 1.我们需要显示一个当前时间的结构 - new Date()
    • 2.我们需要显示当月的信息 - [星期(周一~周日),日期(1-[28,29,30,31])]
      • 其中我们只要知道了每个月的 1日 是星期几,就能很容易地摆放后面的日子(万事开头难)。
      • 我们最多需要 6 行来显示我们的日期,因为要第一排如果只包含本月的一天 6(上个月的) + (1 + 4*7),这样就五行了,当月天数若大于 29,就显示不下了
      • 确定了 6 行之后,我们发现我们可能需要获取上个月,和下个月多出来的几天的摆放位置。
      • 不同年份的不同月的 2月份,我们知道它的日期是不同的,所以我们还需要判断 平年还是闰年。
    • 3.显示上个月,下个月的切换。我们发现需要有个函数来帮我们更新日历。

      这里我们就要考虑一下,到底要怎么更新这些 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;
         80px;
        height: 40px;
        line-height: 40px;
        border: 1px solid sandybrown;
        text-align: center;
      }
      .date-item {
        display: inline-block;
         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(平年的2月)或者29(闰年的2月)或者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);
        if (whereMonday === 0) {
          whereMonday = 7
        }
        // 感谢网友 luoyiming 的测试(哈哈!谢谢!):这里当 whereMonday 为 0 的时候会截取上月的所有数据
        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 框架实现确实还是比较蛋疼的,以下是这次实现的效果。

    实现一个功能的时候,从数据的层面分析,有时候会比较容易理解


    complete.

  • 相关阅读:
    [51nod1384]全排列
    [51nod1256]乘法逆元
    [51nod1106]质数检测
    [51nod1058]求N!的长度
    2017 world final
    [Manacher+bit]Palindrome
    [hdu3068]最长回文(Manacher算法)
    [trie]字典树模板
    [凸包]Triangles
    LintCode-366.斐波纳契数
  • 原文地址:https://www.cnblogs.com/can-i-do/p/11074620.html
Copyright © 2011-2022 走看看