近期在知乎上看到这么一个帖子,题主说自己JavaScript都学完了,结果老师留的作业还是不会写,就是写一个日历的插件,结果楼下一堆大牛出现了,百度的阿里的纷纷站出来发表自己的看法,有人认为简单,有人认为其实细化不简单,于是出于好奇,自己也来动手写了一下。虽说现如今各种优秀的UI框架层出不穷,都会自带calendar和datepick这种日历相关的组件,而且无论是适配还是视觉效果都做得相当nice,可能都不会用到自己写的,但是还是打算动手,因为date对象也是js里面的重点。

第一版:纯js实现
通过切换月份和年份,来展示不同的日历页面,实际上是根据当前年月,来进行页面的重绘,所以页面渲染是一个函数,所需参数就是当前选中的年份和月份。
const render = (month, year) => {
};
render();
首先,日历除去首行day有几行?
一共6行,Math.ceil((31 - 1) / 7 + 1) = 6
三块组成:上月剩余,本月,下月开始
1,上月剩余
首先要知道本月1号是周几(x),然后从周日到x的天数就是上月剩余天数,从几号到几号,需要了解本月是几月,来推算出上月月末是几号,其实也就是上月有多少天。
2,本月
只需知道当月是几月,当月多少天,然后按顺序排。
3,下月开始
只需要知道下月1号是周几,然后42个数字还剩多少,从1排到最后就可以了
 
代码部分:
HTML:
<!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>Date Picker</title>
<link rel="stylesheet" type="text/css" href="css/index.css"></link>
</head>
<body>
<input id="textInput" class="textInput" type="text" placeholder="选择日期" />
<div id="datePicker" class="datePicker datePickerHide">
<div class="dateHeader">
<span id="yearLeft" class="left headerMid"><<</span>
<span id="monthLeft" class="left headerMid page"><</span>
<span id="changeDateHead" class="page">
<span id="changeYear" class="headerMid col">
<span id="dateYear"></span>
<span>年</span>
</span>
<span id="changeMonth" class="headerMid col">
<span id="dateMonth"></span>
<span>月</span>
</span>
</span>
<span id="changeYearHead" class="page headerMid col" style="display: none">
<span id="changeYearFirst"></span>
<span>年</span>
<span>-</span>
<span id="changeYearLast"></span>
<span>年</span>
</span>
<span id="changeMonthHead" class="page headerMid col" style="display: none">
<span id="backChangeYearPage"></span>
<span>年</span>
</span>
<span id="yearRight" class="right headerMid">>></span>
<span id="monthRight" class="right headerMid page">></span>
</div>
<div class="dateMain">
<div id="firstPage" class="page firstPage" style="display: block">
<div class="dateDay">
<span>日</span>
<span>一</span>
<span>二</span>
<span>三</span>
<span>四</span>
<span>五</span>
<span>六</span>
</div>
<div id="dateBody" class="dateBody"></div>
</div>
<div id="secondPage" class="page secondPage" style="display: none"></div>
<div id="thirdPage" class="page secondPage" style="display: none" onclick="chooseMonth()">
<span><em id="month-1" index='1'>1月</em></span>
<span><em id="month-2" index='2'>2月</em></span>
<span><em id="month-3" index='3'>3月</em></span>
<span><em id="month-4" index='4'>4月</em></span>
<span><em id="month-5" index='5'>5月</em></span>
<span><em id="month-6" index='6'>6月</em></span>
<span><em id="month-7" index='7'>7月</em></span>
<span><em id="month-8" index='8'>8月</em></span>
<span><em id="month-9" index='9'>9月</em></span>
<span><em id="month-10" index='10'>10月</em></span>
<span><em id="month-11" index='11'>11月</em></span>
<span><em id="month-12" index='12'>12月</em></span>
</div>
</div>
</div>
</body>
<script src="js/index.js"></script>
</html>

  

js部分:

    // 默认是当天
var chosenDate = new Date(),
year = chosenDate.getFullYear(),
month = chosenDate.getMonth() + 1,
date = chosenDate.getDate(),
lastDateId = '', // 挂到全局下去
lastYearId = '',
lastMonthId = '',
firstNum = 0; window.onload = function(){ renderFirstPage(year, month, date); // input框获取焦点时日历显示 var datePicker = getIdDom('datePicker'); getIdDom('textInput').onfocus = function(){
datePicker.className = 'datePicker datePickerShow';
} /* 以上是第一部分页面展示 */ /* 二级页面 */
var renderSecondPage = function(firstNum){
var lastNum = firstNum + 9; // 二级页面末尾数字
getIdDom('changeYearFirst').innerHTML = firstNum;
getIdDom('changeYearLast').innerHTML = lastNum; var yearTemplate = ``;
for (var i = firstNum; i < lastNum + 1; i++) {
if (i == year) {
// 当前选中年
yearTemplate += `<span><em id="year-${i}" onclick="chooseYear(this, ${i})" style="background-color: #39f;color: #fff">${i}</em></span>`
} else {
yearTemplate += `<span><em id="year-${i}" onclick="chooseYear(this, ${i})">${i}</em></span>`
}
}
getIdDom('secondPage').innerHTML = yearTemplate;
} var reRenderSecondPage = function(){
var yearStr = year.toString();
var yearLastLetter = yearStr[yearStr.length-1]; // 末尾数
firstNum = year - Number(yearLastLetter); // 二级页面首位数字
renderSecondPage(firstNum);
}
reRenderSecondPage(); getIdDom("backChangeYearPage").innerHTML = year; // 三级页面年份 // click事件集中写 // 上一年下一年,上一月下一月的点击事件
clickFn('yearLeft', function(){
if (getIdDom('changeYearHead').style.display != 'none') {
// 此时是二级页面,选年份
if (firstNum - 10 < 1) {
// 首位年份不能小于1
return
}
firstNum -= 10;
renderSecondPage(firstNum);
} else {
if (year - 1 < 1) {
// 年份不能小于1
return
}
year--;
renderFirstPage(year, month, date);
getIdDom("backChangeYearPage").innerHTML = year;
reRenderSecondPage();
}
}); clickFn('monthLeft', function(){
if (month < 2) {
// 1月
month = 12;
year--;
} else {
month--;
}
renderFirstPage(year, month, date)
}); clickFn('yearRight', function(){
if (getIdDom('changeYearHead').style.display != 'none') {
// 此时是二级页面,选年份
firstNum += 10;
renderSecondPage(firstNum);
} else {
year++;
renderFirstPage(year, month, date);
getIdDom("backChangeYearPage").innerHTML = year;
reRenderSecondPage();
}
}); clickFn('monthRight', function(){
if (month > 11) {
// 12月
month = 1;
year++;
} else {
month++;
}
renderFirstPage(year, month, date)
}); clickFn('changeYear', function(){
var pagesArr = Array.from(document.querySelectorAll('.page'));
pagesArr.forEach(function(item){
item.style = 'display: none'
});
reRenderSecondPage();
changeStyle('secondPage', 'display: block');
changeStyle('changeYearHead', 'display: inline-block');
}); // 点击年份切换至二级页面 clickFn('changeMonth', function(){
var pagesArr = Array.from(document.querySelectorAll('.page'));
pagesArr.forEach(function(item){
item.style = 'display: none'
});
if (lastMonthId !== '') {
// 非第一次点击
getIdDom(lastMonthId).style = "";
}
changeStyle("month-" + month, 'background-color: #39f;color: #fff');
lastMonthId = 'month-' + month;
changeStyle('thirdPage', 'display: block');
changeStyle('changeMonthHead', 'display: inline-block');
}) clickFn('changeMonthHead', function(){
// 切回年份选择
var pagesArr = Array.from(document.querySelectorAll('.page'));
pagesArr.forEach(function(item){
item.style = 'display: none'
});
changeStyle('secondPage', 'display: block');
changeStyle('changeYearHead', 'display: inline-block');
reRenderSecondPage();
}) document.getElementsByTagName('html')[0].onclick = function(e){
// 这里模拟失去焦点事件
var name = e.target.nodeName;
if (name == 'BODY' || name == 'HTML') {
datePicker.className = 'datePicker datePickerHide';
}
} } function getIdDom(id){
return document.getElementById(id)
} // 根据id获取dom节点 function clickFn(id, fn) {
getIdDom(id).onclick = fn;
} // 封装一下click方法 function renderFirstPage(year, month, date = 1){
var datePage = []; // 最终展示页面的所有日期合集
// 第一部分,上月月末几天
// 首先要知道上月一共多少天
var getAlldays = (year, month) => {
if (month <= 7) {
// 1-7月
if (month % 2 === 0) {
// 偶数月
if (month === 2) {
// 2月特殊
if ((year % 400 == 0) || ( year % 4 == 0 && year % 100 != 0)) {
// 闰年
var alldays = 29
} else {
var alldays = 28
}
} else {
var alldays = 30
}
} else {
// 奇数月
var alldays = 31
}
} else {
// 8-12月
if (month % 2 === 0) {
// 偶数月
var alldays = 31
} else {
var alldays = 30
}
}
return alldays
}; // alldays表示某年某月的总天数
var lastMonthAllDays = getAlldays(year, month - 1); // 上月天数
var chosenFirstMonthDay = new Date(year, month - 1, 1).getDay(); // 当月1号周几 var datePageTemplate = ``;
var num = 0; for (var i = lastMonthAllDays - chosenFirstMonthDay + 1; i < lastMonthAllDays + 1; i++ ) {
datePageTemplate += `<span id="lastMonth"><em id="last-${i}" onclick="chooseDate(this, 'last-${i}', ${year}, ${month}, ${i})">${i}</em></span>`;
num++;
} // 第二部分,当月总天数
var chosenMonthAllDays = getAlldays(year, month); // 当月天数
var time = new Date();
var a = time.getFullYear(),
b = time.getMonth() + 1,
c = time.getDate(); // 用来记录当天时间
for(var i = 1; i < chosenMonthAllDays + 1; i++) {
var chosenDateObj = {
year: year,
month: month,
date: i
};
if (i === c && year === a && month === b) {
// 今天日期高亮
datePageTemplate += `<span id="today" class="currentMonth"><em id="today-${i}" onclick="chooseDate(this, 'today-${i}', ${year}, ${month}, ${i})" class="today">${i}</em></span>`;
} else {
datePageTemplate += `<span id="currentMonth" class="currentMonth"><em id="cur-${i}" onclick="chooseDate(this, 'cur-${i}', ${year}, ${month}, ${i})">${i}</em></span>`;
} num++;
} // 第三部分,剩余天数
for (var i = 1; i < 43 - num; i++) {
var chosenDateObj = {
year: year,
month: month,
date: i
};
datePageTemplate += `<span id="nextMonth"><em id="nex-${i}" onclick="chooseDate(this, 'nex-${i}', ${year}, ${month}, ${i})">${i}</em></span>`;
} var templateString = `${datePageTemplate}`; var innerFn = function(id, content) {
getIdDom(id).innerHTML = content
};
innerFn('dateYear', year);
innerFn('dateMonth', month);
innerFn('dateBody', templateString); } function chooseDate(item, index, year, month, date) {
event.stopPropagation();
if (lastDateId !== '') {
// 非第一次点击
getIdDom(lastDateId).style = "";
}
// 选中项样式改变,并且将input的日期修改
lastDateId = index;
item.style = "background-color: #39f;color: #fff";
getIdDom("textInput").value = year + '-' + month + '-' + date;
} function chooseYear(item, thisYear) {
event.stopPropagation();
if (lastYearId !== '') {
// 非第一次点击
if (getIdDom(lastYearId)) {
// 存在已经跨页面了,但是id不存在了
getIdDom(lastYearId).style = "";
}
} else {
// 第一次点击
getIdDom('year-' + year).style = ""; }
lastYearId = 'year-' + thisYear;
year = thisYear;
item.style = "background-color: #39f;color: #fff";
var pagesArr = Array.from(document.querySelectorAll('.page'));
pagesArr.forEach(function(item){
item.style = 'display: none'
});
if (lastMonthId !== '') {
// 非第一次点击
getIdDom(lastMonthId).style = "";
}
changeStyle("month-" + month, 'background-color: #39f;color: #fff');
lastMonthId = 'month-' + month;
getIdDom("backChangeYearPage").innerHTML = year;
changeStyle('changeMonthHead', 'display: inline-block');
changeStyle('thirdPage', 'display: block');
} function chooseMonth(){
var target = event.target;
if (target.nodeName === 'EM') {
// 表示当前点击的为em节点
if (lastMonthId !== '') {
// 非第一次点击
getIdDom(lastMonthId).style = "";
} else {
// 第一次点击
getIdDom('month-' + month).style = "";
}
month = parseInt(target.innerHTML);
lastMonthId = 'month-' + month;
target.style = "background-color: #39f;color: #fff"; renderFirstPage(year, month, date); // 展示首页
var pagesArr = Array.from(document.querySelectorAll('.page'));
pagesArr.forEach(function(item){
item.style = 'display: none'
});
changeStyle('firstPage', 'display: block');
changeStyle(['changeDateHead', 'monthLeft', 'monthRight'], 'display: inline-block');
}
} // 判断对象类型
function isType(type){
return function(obj){
return toString.call(obj) == '[object ' + type + ']';
}
} // 改变display属性
function changeStyle(ids, styles){
var isString = isType('String'),
isArray = isType('Array');
if (isString(ids)) {
getIdDom(ids).style = styles;
} else if (isArray(ids)) {
ids.forEach(function(item){
getIdDom(item).style = styles;
})
}
}

  

  

css部分:

* {
margin: 0;
padding: 0;
} *, :after, :before {
box-sizing: border-box;
} body {
font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,\\5FAE\8F6F\96C5\9ED1,Arial,sans-serif;
} .textInput {
position: relative;
display: block;
} .datePicker {
width: 216px;
margin: 10px;
color: #c3cbd6;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0,0,0,.2);
transform-origin: center top 0px;
transition: all .2s ease-in-out;
position: absolute;
left: 0px;
top: 16px;
}
.datePickerHide {
opacity: 0;
} .datePickerShow {
opacity: 1;
} .dateHeader {
height: 32px;
line-height: 32px;
text-align: center;
border-bottom: 1px solid #e3e8ee;
} .left, .right {
display: inline-block;
width: 20px;
height: 24px;
line-height: 26px;
margin-top: 4px;
text-align: center;
cursor: pointer;
color: #c3cbd6;
-webkit-transition: color .2s ease-in-out;
transition: color .2s ease-in-out;
} .left {
float: left;
margin-left: 10px;
} .right {
float: right;
margin-right: 10px;
} .dateMain {
margin: 10px;
} .dateDay {
line-height: normal;
font-size: 0;
letter-spacing:normal;
} span {
display: inline-block;
text-align: center;
font-size: 12px;
} .dateDay span {
line-height: 24px;
width: 24px;
height: 24px;
margin: 2px;
} .dateBody span {
width: 28px;
height: 28px;
cursor: pointer;
} .dateBody span em {
display: inline-block;
position: relative;
width: 24px;
height: 24px;
line-height: 24px;
margin: 2px;
font-style: normal;
border-radius: 3px;
text-align: center;
transition: all .2s ease-in-out;
} .dateBody span.currentMonth {
color: #657180;
} .today:after {
content: '';
display: block;
width: 6px;
height: 6px;
border-radius: 50%;
background: #39f;
position: absolute;
top: 1px;
right: 1px;
} .currentMonth em:hover {
background-color: #e1f0fe;
} .col {
color: #657180;
} .headerMid {
cursor: pointer;
} .headerMid:hover {
color: #39f;
} /* second */
.secondPage {
width: 196px;
font-size: 0;
} .secondPage span {
display: inline-block;
width: 40px;
height: 28px;
line-height: 28px;
margin: 10px 12px;
border-radius: 3px;
cursor: pointer;
} .secondPage span em {
display: inline-block;
width: 40px;
height: 28px;
line-height: 28px;
margin: 0;
font-style: normal;
border-radius: 3px;
text-align: center;
transition: all .2s ease-in-out;
color: #657180;
} .secondPage span em:hover {
background-color: #e1f0fe;
}

  

GitHub项目地址:https://github.com/Yanchenyu/DatePicker

 
 
项目写完了,但其实发现里面存在大量的DOM操作以及环境污染,这个对性能的损耗是相当大的,写得越多越发现状态难以管理,都只能挂到全局下去,所以打算再写一套React版本的。
 
 
 
 
 
 
 
 
 
 
end

JavaScript日历(es5版本)的更多相关文章

  1. JavaScript日历控件开发 C# 读取 appconfig文件配置数据库连接字符串,和配置文件 List<T>.ForEach 调用异步方法的意外 ef 增加或者更新的习惯思维 asp.net core导入excel 一个二级联动

    JavaScript日历控件开发   概述 在开篇之前,先附上日历的代码地址和演示地址,代码是本文要分析的代码,演示效果是本文要实现的效果代码地址:https://github.com/aspwebc ...

  2. Javascript 日历插件

    1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点击下载    查看示例 2. DatePicker 这款日期插件支持单选.多选和 ...

  3. 12款优秀的 JavaScript 日历和时间选择控件

    这些插件能够帮助  Web 开发人员更快速的实现各种精美的日历和时间选择效果. 1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点 ...

  4. 推荐一款JavaScript日历控件:kimsoft-jscalendar

    一.什么是 kimsoft-jscalendar     一个简洁的avaScript日历控件,可在Java Web项目,.NET Web 项目中使用 二.kimsoft-jscalendar 有什么 ...

  5. javascript日历插件

    原文:javascript日历插件 javascript日历插件 最近在尝试着写javascript日历插件,所以也到github上看国外人日历源码,或者国内人写的好点的,也在研究点,虽然看到网上有一 ...

  6. JavaScript获取浏览器版本等信息

    ** 不同浏览器版本可能存在差异,使用时请测试自己的环境 ** 测试时各个浏览器版本 IE: 11.953.14393.0 Edge: Microsoft Edge 38.14393.0.0;Micr ...

  7. javaScript - 面向对象 - ES5 和 ES6

    javaScript - 面向对象 - ES5 和 ES6 ES5之前用 构造函数 构造函数的特点 就是一个普通函数, 他的函数名要大写.: 带方法的写法: 原型的方式: prototype 为内置的 ...

  8. 【转】浅谈JavaScript、ES5、ES6

    什么是JavaScript JavaScript一种动态类型.弱类型.基于原型的客户端脚本语言,用来给HTML网页增加动态功能.(好吧,概念什么最讨厌了) 动态: 在运行时确定数据类型.变量使用之前不 ...

  9. JavaScript、ES5和ES6的介绍和区别

    JavaScript由三部分组成: ECMAScript(核心) DOM(文档对象模型) BOM (浏览器对象模型) ES5(ECMAScript第五个版本) strict模式 严格模式,限制一些用法 ...

随机推荐

  1. Python模块subprocess

    subprocess的常用用法 """ Description: Author:Nod Date: Record: #-------------------------- ...

  2. docker入门 什么是docker? 为什么使用docker?

    1.什么是docker? 轻量级操作系统虚拟化解决方案 2.为什么使用docker? 1.docker的启动是秒级的,比传统虚拟机快很多 2.资源利用率高,一台主机上可同时运行数千个docker容器 ...

  3. mybatis的typeHandler

    typeHandler作用: 1.传参时将javaType类型转换成jdbcType 2.结果集中ResultSet中取值时,jdbcType转换为javaType; 系统自定义的typeHandle ...

  4. 安装grid时找不到ASM共享磁盘

    1.安装ORACLE数据库集群软件grid时找不到共享磁盘,如下图: 2.网上找过有各种说法,但此处小编的解决方案是:通过重新安装软件:oracleasmlib-2.0.4-1.el6.x86_64. ...

  5. JS截取URL地址参数

    var url = window.location.search; 截取?r= 后面的参数var url = window.location.href;var urlss= urlssplit('co ...

  6. maven构建SSM框架中pom.xml文件配置

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  7. leetcode647

    class Solution { public: ][],int i,int j){ if(i>=j){ return true; } else{ return DP[i][j]; } } in ...

  8. python中类与对象之继承

    面对对象的三大特性之继承 1.什么是继承? 在程序中,继承指的是class与class之间的关系 继承是一种关系,必须存在两个class才能产生这种关系:被继承的class称为父类,继承的class称 ...

  9. 移动平台MOBA发热与帧率优化

    MOBA项目的优化进入到了第二阶段,千元机,发热严重问题处理,及帧率进一步提升. 回顾之前的优化,当初我的 OPPO R9S不过8-10帧,后来经过了逻辑计算的一些优化后达到10-20帧. 再后来开启 ...

  10. python入门(七):字符串

    1.字符串类型: >>> s="早上好"               #str类型的字符串 >>> type(s) <class 'str ...