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

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

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

首先,我们看看 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;
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 框架实现确实还是比较蛋疼的,以下是这次实现的效果。

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


complete.

原生JS实现日历的更多相关文章

  1. 原生js简易日历效果实现

    这里我们将用原生js实现简易的日历,原理和之前的原生js选项卡差不多,不过也有些区别: 首先html代码: <div class="container"> <di ...

  2. 创建一个js日历(原生JS实现日历)

    前言 方法是有参考网上一些资料的,比如闰年的判断,比如每个月第一天是星期几的判断.不说太多,拆分出一个个函数,希望能描述尽可能的清晰明了. 一,判断闰年 //判断闰年 function runNian ...

  3. 日历组件 原生js

    自己基于原生js编写的日历组件 git地址: https://github.com/lihefen/calendar.git demo : https://lihefen.github.io/cale ...

  4. 原生js日历选择器,学习js面向对象开发日历插件

    在web开发过程中经常会碰到需要选择日期的功能,一般的操作都是在文本框点击,然后弹出日历选择框,直接选择日期就可以在文本框显示选择的日期.开发好之后给用户使用是很方便,但如果每一个日历选择器都要临时开 ...

  5. 原生JS封装Ajax插件(同域&&jsonp跨域)

    抛出一个问题,其实所谓的熟悉原生JS,怎样的程度才是熟悉呢? 最近都在做原生JS熟悉的练习... 用原生Js封装了一个Ajax插件,引入一般的项目,传传数据,感觉还是可行的...简单说说思路,如有不正 ...

  6. 常用原生JS方法总结(兼容性写法)

    经常会用到原生JS来写前端...但是原生JS的一些方法在适应各个浏览器的时候写法有的也不怎么一样的... 今天下班有点累... 就来总结一下简单的东西吧…… 备注:一下的方法都是包裹在一个EventU ...

  7. 原生JS实现"旋转木马"效果的图片轮播插件

    一.写在最前面 最近都忙一些杂七杂八的事情,复习软考.研读经典...好像都好久没写过博客了... 我自己写过三个图片轮播,一个是简单的原生JS实现的,没有什么动画效果的,一个是结合JQuery实现的, ...

  8. 再谈React.js实现原生js拖拽效果

    前几天写的那个拖拽,自己留下的疑问...这次在热心博友的提示下又修正了一些小小的bug,也加了拖拽的边缘检测部分...就再聊聊拖拽吧 一.不要直接操作dom元素 react中使用了虚拟dom的概念,目 ...

  9. React.js实现原生js拖拽效果及思考

    一.起因&思路 不知不觉,已经好几天没写博客了...近来除了研究React,还做了公司官网... 一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨.所以就用react来实现这个拖 ...

随机推荐

  1. RTP Payload Format for Opus Speech and Audio Codec

    [Docs] [txt|pdf] [Tracker] [WG] [Email] [Diff1] [Diff2] [Nits] Versions: (draft-spittka-payload-rtp- ...

  2. bzoj 3689: 异或之 Trie+堆

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=3689 题解: 利用一个优先队列存储当前取到的数 然后再写一颗支持查找异或的k大值的Tri ...

  3. 强烈建议使用国外DNS解析域名,解决访问速度和某些访问故障!

    域名解析的基本原理是把域名翻译成IP地址,以便计算机能够进一步通信,传递网址和内容等. 域名劫持就是在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则直接返回假的IP ...

  4. 洛谷【P1175】表达式的转换

    浅谈栈:https://www.cnblogs.com/AKMer/p/10278222.html 题目传送门:https://www.luogu.org/problemnew/show/P1175 ...

  5. rsync 端口更换(默认873)

    一般使用默认端口的话, 在服务端的启动命令为: /usr/bin/rsync --address=192.168.1.23 --daemon 如果在客户端需要换另外的端口侦听, 则使用 /usr/bi ...

  6. Mybatis下面的MapperScannerConfigurer 扫描器

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring Mybatis在与Spring集成的时候可以配置              Ma ...

  7. Java常见设计模式之观察者模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述观察者(Observer)模式的: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Mo ...

  8. 【转】Ruby on Rails中select使用方法

    在Ruby on Rails中真的有一堆Select helper可以用,我们经常容易混淆.常见的有三个..select, select_tag, collection_select(其余的什么sel ...

  9. pyodbc

  10. shell入门-wc

    命令:wc 选项:-l  查看行数 -w  以空白字符为分隔符 查看有多少单词 -m  查看字符数,文件大小 说明:统计指定文件中的字节数.字数.行数. -l [root@wangshaojun 11 ...