js实现日历


Posted in Javascript onNovember 07, 2020

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

首先,我们看看 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.显示上个月,下个月的切换。我们发现需要有个函数来帮我们更新日历。

分析完之后,让我们跟着 新增/修改 一些代码。

<!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(平年的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 框架实现确实还是比较蛋疼的,以下是这次实现的效果。

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

以上就是js实现日历的详细内容,更多关于js 日历的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
jQuery侧边栏随窗口滚动实现方法
Mar 04 Javascript
JS动态修改表格cellPadding和cellSpacing的方法
Mar 31 Javascript
js计算文本框输入的字符数
Oct 23 Javascript
JavaScript模拟push
Mar 06 Javascript
JavaScript日期工具类DateUtils定义与用法示例
Sep 03 Javascript
微信小程序实现笑脸评分功能
Nov 03 Javascript
layui异步加载table表中某一列数据的例子
Sep 16 Javascript
js模拟F11页面全屏显示
Sep 17 Javascript
JavaScript Canvas编写炫彩的网页时钟
Oct 16 Javascript
VUE 直接通过JS 修改html对象的值导致没有更新到数据中解决方法分析
Dec 02 Javascript
Vue Render函数创建DOM节点代码实例
Jul 08 Javascript
浅谈JS的二进制家族
May 09 Javascript
工作中常用js功能汇总
Nov 07 #Javascript
解决VUE 在IE下出现ReferenceError: Promise未定义的问题
Nov 07 #Javascript
解决Element中el-date-picker组件不回填的情况
Nov 07 #Javascript
解决element-ui的下拉框有值却无法选中的情况
Nov 07 #Javascript
解决VUE项目使用Element-ui 下拉组件的验证失效问题
Nov 07 #Javascript
详解datagrid使用方法(重要)
Nov 06 #Javascript
VUE异步更新DOM - 用$nextTick解决DOM视图的问题
Nov 06 #Javascript
You might like
php pcntl_fork和pcntl_fork 的用法
2009/04/13 PHP
PHP编程基本语法快速入门手册
2016/01/07 PHP
PHP简单实现DES加密解密的方法
2016/07/12 PHP
php实现的网页版剪刀石头布游戏示例
2016/11/25 PHP
TP3.2.3框架使用CKeditor编辑器在页面中上传图片的方法分析
2019/12/31 PHP
Javascript实例教程(19) 使用HoTMetal(6)
2006/12/23 Javascript
Javascript-Mozilla和IE中的一个函数直接量的问题分析
2007/08/12 Javascript
javascript网页关闭时提醒效果脚本
2008/10/22 Javascript
js读取csv文件并使用json显示出来
2015/01/09 Javascript
jQuery实现可移动选项的左右下拉列表示例
2016/12/26 Javascript
bootstrap配合Masonry插件实现瀑布式布局
2017/01/18 Javascript
javascript实现秒表计时器的制作方法
2017/02/16 Javascript
vue2 自定义动态组件所遇到的问题
2017/06/08 Javascript
ES6正则表达式扩展笔记
2017/07/25 Javascript
微信小程序学习笔记之目录结构、基本配置图文详解
2019/03/28 Javascript
使用eslint和githooks统一前端风格的技巧
2020/07/29 Javascript
vant picker+popup 自定义三级联动案例
2020/11/04 Javascript
[02:14]DOTA2英雄基础教程 修补匠
2013/12/23 DOTA
Python初学时购物车程序练习实例(推荐)
2017/08/08 Python
socket + select 完成伪并发操作的实例
2017/08/15 Python
python和shell获取文本内容的方法
2018/06/05 Python
关于Python中的向量相加和numpy中的向量相加效率对比
2019/08/26 Python
opencv resize图片为正方形尺寸的实现方法
2019/12/26 Python
python 实现简单的FTP程序
2019/12/27 Python
Python super()方法原理详解
2020/03/31 Python
python程序实现BTC(比特币)挖矿的完整代码
2021/01/20 Python
CSS3中:nth-child和:nth-of-type的区别深入理解
2014/03/10 HTML / CSS
加拿大女包品牌:Matt & Nat
2017/05/12 全球购物
英国独特家具和家庭用品购物网站:Cuckooland
2020/08/30 全球购物
聚美优品陈欧广告词
2014/03/14 职场文书
《去年的树》教学反思
2014/04/11 职场文书
个人总结与自我评价
2014/09/18 职场文书
大学生实习证明范文(5篇)
2014/09/18 职场文书
企业员工辞职信范文
2015/05/12 职场文书
读《教育心理学》心得体会
2016/01/22 职场文书
MySQL连接查询你真的学会了吗?
2021/06/02 MySQL