近期在知乎上看到这么一个帖子,题主说自己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. C++中宽字符类型(wchar_t)的编码

    转载自: http://www.ituring.com.cn/article/111027 问题的起因是和一个朋友讨论不同编码的转换问题,说到了wchar_t的类型,朋友的看法是,wchar_t的编码 ...

  2. DateTimeOffset DateTime

    DateTime只保存两部分信息:Ticks和KindTicks 一个Tick是100纳秒(1万Tick等于1毫秒)Ticks记录了从1/1/0001 12:00 AM到现在经过了多少100纳秒.Ki ...

  3. CentOS 7 实现ssh无密码登录

    cd ~ 进入根目录. (使用ls -a或者 ls -la 能够看到当前文件夹下的所有文件包含隐藏文件夹等) 我们首先使用ls -la  发现并没有.ssh的文件夹存在. 在终端输入   ssh lo ...

  4. 国内+海外IDC资源合作

    主营业务:服务器租用.托管.机柜大带宽.安全防御.云主机.海外专线.海外托管.CDN加速.站群 资源覆盖: 华南:广东东莞.深圳.广州.湛江.福建厦门.泉州.福州 华北:北京.天津.山东 华东:江苏苏 ...

  5. PHP生成HTML静态页面。

    function Generate(){ $html = '<!DOCTYPE html><html lang="en"><head> < ...

  6. CentOS 7 无法yum安装解决方法

    1)下载repo文件 wget http://mirrors.aliyun.com/repo/Centos-7.repo 2)备份并替换系统的repo文件 .repo /etc/yum.repos.d ...

  7. idea导入项目

    1. 2.导入项目 3.右键项目选择web 4.编辑添加tomcat 5.添加jar.包 6. 7.右键put into 8.安装tomcat 9.引入tomcat 10.把项目布署到tomcat

  8. python入门学习0

    Python 是什么类型的语言 Python是脚本语言 Python下载地址:https://www.python.org/downloads/ Python版本:Python 3.4.2 - 64b ...

  9. 25.Hibernate-配置文件.md

    目录 1.主配置文件 1.1定义 1.1.1分类 1.1.2分类 1.1.3不使用配置文件生成表 1.2教程 2. 映射配置文件 1.主配置文件 1.1定义 1.1.1分类 在hibernate的配置 ...

  10. SpringCloud详解

    来源于纯洁的微笑 http://www.ityouknow.com/spring-cloud.html