当我们需要在页面中显示某月的事项,或是选择某一段日期时,常常要使用到日历组件。这一组件同样有着许多现成的类库,然而亲自动手开发一个日历,从中了解其实现原理也是非常必要的。在本例中我们就将制作一款非常经典的日历组件。

更多精彩内容欢迎访问我的个人博客皮皮猪:http://www.zhsh666.xyz或者http://www.zh66.club期待您的光临哦!我是皮皮猪,感谢各位光临,能为您排忧解难小站深感荣幸!祝您生活愉快!

1. 创建DOM代码

本例的HTML代码如下:

<div class="calendar">
<div class="title">
<h1 class="green" id="calendar-title">Month</h1>
<h2 class="green small" id="calendar-year">Year</h2>
<a href="" id="prev">Prev Month</a>
<a href="" id="next">Next Month</a>
</div>
<div class="body">
<div class="lightgrey body-list">
<ul>
<li>MON</li>
<li>TUE</li>
<li>WED</li>
<li>THU</li>
<li>FRI</li>
<li>SAT</li>
<li>SUN</li>
</ul>
</div>
<div class="darkgrey body-list">
<ul id="days">
</ul>
</div>
</div>
</div>

由以上代码可见,日历的最外层是一个类名为calendar的div元素,其内部包含了两大部分,分别是日历顶部的标题区域,其类名为title,以及其下方的日期区域,其类名为body。

在title中包含了当前所选日期的月份、年份,其id分别为calendar-title和calendar-year,此外该元素还包括了prev和next两个按钮,分别用于切换选择上一月和下一月。

在body中首先包含了一个周一到周日的英文表头,它们放在一个ul元素中。在表头之后是另一个div元素,用于显示日历主体。其中,日历所选月份的每一天都将显示在该元素内部的days列表中。

2. CSS样式

本例中大部分的基础CSS样式省略介绍,在此仅介绍其中的body-list类。该类用于设置日历表头和日期数据的栅格显示。我们将整个ul元素的宽度设置为100%,并将其除以7,就可以得到每一个li元素的宽度,即14.28%,将这些元素左浮动显示,就可以得到日期的7列显示,样式代码如下:

.calendar{
width:450px;
height:350px;
background:#fff;
box-shadow:0px 1px 1px rgba(0,0,0,0.1);
}
.body-list ul{
width:100%;
font-family:arial;
font-weight:bold;
font-size:14px;
}
.body-list ul li{
width:14.28%;
height:36px;
line-height:36px;
list-style-type:none;
display:block;
box-sizing:border-box;
float:left;
text-align:center;
}

此外,我们为今天、今天之前及今天之后的日期分别创建了不同的类。其中,用浅灰色来显示过去的日期,深灰色来显示将来的日期,日期当天则使用浅绿色背景、绿色文字加以显示,样式代码如下:

.lightgrey{
color:#a8a8a8; /*浅灰色*/
}
.darkgrey{
color:#565656; /*深灰色*/
}
.green{
color:#6ac13c; /*绿色*/
}
.greenbox{
border:1px solid #6ac13c;
background:#e9f8df; /*浅绿色背景*/
}

日历组件的初始显示效果如下图所示。

3. JavaScript生成日历

接着使用JavaScript来动态生成日历信息。首先,我们要做一些前期的准备工作。由于闰年和非闰年的二月天数是不一样的,在此我们为这两种年份分别创建数组,以便于获取每个月的天数,同时为每个月份的英文名创建相应的数组变量,代码如下:

var month_olympic = [31,29,31,30,31,30,31,31,30,31,30,31];
var month_normal = [31,28,31,30,31,30,31,31,30,31,30,31];
var month_name = ["January","Febrary","March","April","May","June","July","Auguest","September","October","November","December"];

然后,为页面中的各种元素创建变量,以便于后续的引用,代码如下:

var holder = document.getElementById("days");
var prev = document.getElementById("prev");
var next = document.getElementById("next");
var ctitle = document.getElementById("calendar-title");
var cyear = document.getElementById("calendar-year");

创建一个Date对象来获取当前的日期时间,并通过getFullYear()方法来获取当前年份,getMonth()方法来获取月份,getDate()方法来获取当前日期。代码如下:

var my_date = new Date();
var my_year = my_date.getFullYear();
var my_month = my_date.getMonth();
var my_day = my_date.getDate();

我们要实现日历的排布,最关键的问题是要知道某一月第一天究竟是星期几,然后才可以根据当月的天数来依次排列其后的日期。在此,专门为该功能创建一个函数,代码如下:

//获取某年某月第一天是星期几
function dayStart(month, year) {
var tmpDate = new Date(year, month, 1);
return (tmpDate.getDay());
}

此外,我们也创建一个相应的函数来获取某月的总天数,代码如下:

//计算某年是不是闰年,通过求年份除以4的余数即可
function daysMonth(month, year) {
var tmp = year % 4;
if (tmp == 0) {
return (month_olympic[month]);
} else {
return (month_normal[month]);
}
}

然后,创建一个refreshDate函数来生成月份显示,代码如下:

function refreshDate(){
var str = "";
var totalDay = daysMonth(my_month, my_year); //获取该月总天数
var firstDay = dayStart(my_month, my_year); //获取该月第一天是星期几
var myclass;
for(var i=1; i<firstDay; i++){
str += "<li></li>"; //为起始日之前的日期创建空白节点
}
for(var i=1; i<=totalDay; i++){
if((i<my_day && my_year==my_date.getFullYear() && my_month==my_date.getMonth()) || my_year<my_date.getFullYear() || ( my_year==my_date.getFullYear() && my_month<my_date.getMonth())){
myclass = " class='lightgrey'"; //当该日期在今天之前时,以浅灰色字体显示
}else if (i==my_day && my_year==my_date.getFullYear() && my_month==my_date.getMonth()){
myclass = " class='green greenbox'"; //当天日期以绿色背景突出显示
}else{
myclass = " class='darkgrey'"; //当该日期在今天之后时,以深灰字体显示
}
str += "<li"+myclass+">"+i+"</li>"; //创建日期节点
}
holder.innerHTML = str; //设置日期显示
ctitle.innerHTML = month_name[my_month]; //设置英文月份显示
cyear.innerHTML = my_year; //设置年份显示
}
refreshDate(); //执行该函数

测试页面,日历显示效果如下图所示。

4. 添加交互动作

最后,我们为prev和next元素分别创建onclick事件函数,使得每点击一次prev,则将当前月份减去1,并调用refreshDate函数刷新日历显示。当月份数值小于0时,则使年份减去1,并使月份变为11,使日历显示为前一年的12月。next的功能与其恰好相反。代码如下:

prev.onclick = function(e){
e.preventDefault();
my_month--;
if(my_month<0){
my_year--;
my_month = 11;
}
refreshDate();
}
next.onclick = function(e){
e.preventDefault();
my_month++;
if(my_month>11){
my_year++;
my_month = 0;
}
refreshDate();
}

测试页面,日历中前一月、后一月的切换效果如下图所示。

下载源文件:https://cloud.ecnu.edu.cn/p/DVF08RwQGRi6Aw

最后,希望这篇文章对大家自定义开发页面中的日历组件有所帮助!

HTML+JavaScript自己动手做日历的更多相关文章

  1. 动手做第一个Chrome插件

    Chrome插件是令人惊讶的简单,一旦你弄懂它的工作和实现原理.它是由一部分HTML,一部分Js,然后混合了一个叫做manifest.json的Json文件组合而成的整体.这意味着你可以使用你最擅长的 ...

  2. 「雕爷学编程」Arduino动手做(29)——DS1302时钟模块

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...

  3. IP工具类-自己动手做个ip解析器

    IP工具类-自己动手做个ip解析器 一.资料准备 导入依赖包:

  4. 从.net复制源代码中国农历阵列,必要做日历

    从.net复制源代码中国农历阵列,必要做日历 const { 闰月的月份.春节的阳历日期(农历正月初一).农历的每一个月天数 } c_arrLunarInfo: array [1900 .. 2100 ...

  5. 自己动手做AI:Google AIY开发工具包解析

    2018年国际消费性电子展(CES)上,最明显的一个趋势是Amazon与Google的语音技术进驻战,如AmazonAlexa进驻到Acer笔电内,Google Assist进驻到KIA汽车内,其他如 ...

  6. 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转

    线程安全使用(四)   这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...

  7. 生活娱乐 WIFI机器人(某机器发烧友自己动手做一台)

    某机器发烧友自己动手做一台WIFI机器人,以下是这位发烧友的自述!让我们一起来分享他的劳动成果-- 在经历了十多天的疯狂淘宝.组装.调试.拆卸.再组装.再调试的过程后,俺的Wifi Robot终于于2 ...

  8. 「雕爷学编程」Arduino动手做(9)——火焰传感器模块

    37款传感器和模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器与模块,依照实践出真知(动手试试)的理念,以学习和交流为目的,这里准备 ...

  9. 「雕爷学编程」Arduino动手做(10)——敲击传感器模块

    37款传感器和模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器与模块,依照实践出真知(动手试试)的理念,以学习和交流为目的,这里准备 ...

随机推荐

  1. 深入理解Java中的锁(三)

    ReadWriteLock接口 读写锁维护一对关联锁,一个只用于读操作,一个只用于写操作.读锁可以由多个线程同时持有,又称共享锁.写锁同一时间只能由一个线程持有,又称互斥锁.同一时间,两把锁不能被不同 ...

  2. Android的简述4

    NoteEditor深入分析 首先来弄清楚“日志编辑“的状态转换,通过上篇文章的方法来做下面这样一个实验,首先进入“日志编辑“时会触发onCreate和onResume,然后用户通过Option Me ...

  3. perspective transform透视矩阵快速求法+矩形矫正

    算了半天一直在思考如何快速把矩阵算出来,网上基本都是在说边长为1的正方形的变换方式=.=   不怎么用得上…… 公式推导推半天,计算还麻烦.... ++++++++++++++++++++++++++ ...

  4. 【Android】System.exit(0) 退出程序

    许多 Android 应用程序都是连续点击两下返回键时退出程序,代码如下: private long exitTime = 0; @Override public boolean onKeyDown( ...

  5. Another option to bootup evidence files

    When it comes to booting up evidence files acquired from target disk, you got two options. One is VF ...

  6. java遍历所有目录和文件

    package xian; import java.io.File; import java.util.ArrayList; public class GetFile { private static ...

  7. Mysql的行级锁与表级锁

    在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足. 在DBMS中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎).表级锁(MYISAM ...

  8. RabbitMQ与spring集成,配置完整的生产者和消费者

    RabbitMQ与AMQP协议详解可以看看这个 http://www.cnblogs.com/frankyou/p/5283539.html 下面是rabbitMQ和spring集成的配置,我配置了二 ...

  9. koa2基于stream(流)进行文件上传和下载

    阅读目录 一:上传文件(包括单个文件或多个文件上传) 二:下载文件 回到顶部 一:上传文件(包括单个文件或多个文件上传) 在之前一篇文章,我们了解到nodejs中的流的概念,也了解到了使用流的优点,具 ...

  10. Spring Cloud 相关资料链接

    Spring Cloud中文网:https://springcloud.cc/ Spring Cloud API:https://springcloud.cc/spring-cloud-dalston ...