JavaScript日历(es5版本)
近期在知乎上看到这么一个帖子,题主说自己JavaScript都学完了,结果老师留的作业还是不会写,就是写一个日历的插件,结果楼下一堆大牛出现了,百度的阿里的纷纷站出来发表自己的看法,有人认为简单,有人认为其实细化不简单,于是出于好奇,自己也来动手写了一下。虽说现如今各种优秀的UI框架层出不穷,都会自带calendar和datepick这种日历相关的组件,而且无论是适配还是视觉效果都做得相当nice,可能都不会用到自己写的,但是还是打算动手,因为date对象也是js里面的重点。
<!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
JavaScript日历(es5版本)的更多相关文章
- JavaScript日历控件开发 C# 读取 appconfig文件配置数据库连接字符串,和配置文件 List<T>.ForEach 调用异步方法的意外 ef 增加或者更新的习惯思维 asp.net core导入excel 一个二级联动
JavaScript日历控件开发 概述 在开篇之前,先附上日历的代码地址和演示地址,代码是本文要分析的代码,演示效果是本文要实现的效果代码地址:https://github.com/aspwebc ...
- Javascript 日历插件
1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点击下载 查看示例 2. DatePicker 这款日期插件支持单选.多选和 ...
- 12款优秀的 JavaScript 日历和时间选择控件
这些插件能够帮助 Web 开发人员更快速的实现各种精美的日历和时间选择效果. 1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点 ...
- 推荐一款JavaScript日历控件:kimsoft-jscalendar
一.什么是 kimsoft-jscalendar 一个简洁的avaScript日历控件,可在Java Web项目,.NET Web 项目中使用 二.kimsoft-jscalendar 有什么 ...
- javascript日历插件
原文:javascript日历插件 javascript日历插件 最近在尝试着写javascript日历插件,所以也到github上看国外人日历源码,或者国内人写的好点的,也在研究点,虽然看到网上有一 ...
- JavaScript获取浏览器版本等信息
** 不同浏览器版本可能存在差异,使用时请测试自己的环境 ** 测试时各个浏览器版本 IE: 11.953.14393.0 Edge: Microsoft Edge 38.14393.0.0;Micr ...
- javaScript - 面向对象 - ES5 和 ES6
javaScript - 面向对象 - ES5 和 ES6 ES5之前用 构造函数 构造函数的特点 就是一个普通函数, 他的函数名要大写.: 带方法的写法: 原型的方式: prototype 为内置的 ...
- 【转】浅谈JavaScript、ES5、ES6
什么是JavaScript JavaScript一种动态类型.弱类型.基于原型的客户端脚本语言,用来给HTML网页增加动态功能.(好吧,概念什么最讨厌了) 动态: 在运行时确定数据类型.变量使用之前不 ...
- JavaScript、ES5和ES6的介绍和区别
JavaScript由三部分组成: ECMAScript(核心) DOM(文档对象模型) BOM (浏览器对象模型) ES5(ECMAScript第五个版本) strict模式 严格模式,限制一些用法 ...
随机推荐
- JDBC 中preparedStatement和Statement区别
一.概念 PreparedStatement是用来执行SQL查询语句的API之一,Java提供了 Statement.PreparedStatement 和 CallableStatement三种方式 ...
- 阿里Java开发手册
1.1 命名风格 (1)常量命名全部大写,单词间用下划线隔开. (2)抽象类命名以Abstract或Base开头:异常类命名以Exception结尾:测试类命名以它要测试的类名开始,以Test结尾. ...
- 大数据的乘法实现——C语言
1大数据乘法的算法思路: 输入两个字符串,得到结果,例如:123456789*123456789: 思路:1)首先 123456789*1 = 9 18 27 36 45 54 63 ...
- CentOS7.5 安装ssh
yum -y install openssh-clients 如果出现 Permissions 0644 for ‘/root/.ssh/id_rsa’ are too open. 等错误显示了,原来 ...
- jquery.cookie用法及其注意点
jquery.cookie是一个轻量级的cookie插件,由于已被封装好,可拿来即用. 基本的创建.读取.删除见另一篇文章 浅谈localStorage.sessionStorage 与cookie ...
- python大法好——Python 面向对象
Python 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的. 面向对象技术简介 类(Class): 用来描述具有相同的属性和方法 ...
- JavaScript学习-1
本章目录: --------①数据类型. --------②定义变量. --------③类型转换. --------④运算符. --------⑤比较符. --------⑥if语句. ------ ...
- 深度学习原理与框架-Tensorflow卷积神经网络-cifar10图片分类(代码) 1.tf.nn.lrn(局部响应归一化操作) 2.random.sample(在列表中随机选值) 3.tf.one_hot(对标签进行one_hot编码)
1.tf.nn.lrn(pool_h1, 4, bias=1.0, alpha=0.001/9.0, beta=0.75) # 局部响应归一化,使用相同位置的前后的filter进行响应归一化操作 参数 ...
- 记账本,C,Github,entity
package entity; public class Category { private int id; private String name; private int recordNumbe ...
- ABAP 省市县级联搜索帮助
在展示ABAP代码之前,先建立自建表ZCHENH006,表中包含两个关键字段 BELNR(地区编码),SDESC(地区描述). 编码规则参考:身份证前六位地区编码规则,可参考我另外一篇Blog导入系统 ...