todolist实现删除的功能_原生JS实现一个日期选择器(DatePicker)组件

d3540b6f-7913-eb11-8da9-e4434bdf6706.png

前言:最近看到慕课网上有一个实现日期选择器组件的课程,空闲时间就看了一下,觉得还是有一些借鉴之处,在参考了一下其他文章,写下了这篇文章。通过自己的理解将整个实现过程以最简单的语言描述出来,争取让小白也能看懂。文章中可能会出现许多拓展知识,千万不要错过哦!

文章的知识点:通过原生HTML/CSS/JavaScript完成一个日期选择器(datepicker)组件的开发。主要包括datepicker静态结构的编写、日历数据的计划获取、组件的渲染以及组件事件的处理。

其它参考文章:

天之蓝源:三分钟在GitHub上搭建个人博客​zhuanlan.zhihu.com
e5540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:零基础Hexo+Github搭建静态个人博客​zhuanlan.zhihu.com
e8540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生js实现点击按钮复制文本内容​zhuanlan.zhihu.com
e9540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:九种跨域方式实现原理​zhuanlan.zhihu.com
ea540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:前端面试考点多?看这些文章就够了(转载)​zhuanlan.zhihu.com
eb540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:干货!值得收藏的前端学习网站​zhuanlan.zhihu.com
ee540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生JS实现一个日期选择器(DatePicker)组件​zhuanlan.zhihu.com
f3540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生js一步一步实现《别踩白块儿》小游戏​zhuanlan.zhihu.com
f8540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生js利用localstorage实现简易TODO list应用​zhuanlan.zhihu.com
fb540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生js实现瀑布流效果​zhuanlan.zhihu.com
fd540b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生js实现图片懒加载(lazyLoad)​zhuanlan.zhihu.com
01550b6f-7913-eb11-8da9-e4434bdf6706.png
天之蓝源:原生js实现简单路由切换​zhuanlan.zhihu.com
04550b6f-7913-eb11-8da9-e4434bdf6706.png
实现效果:

实现效果:

05550b6f-7913-eb11-8da9-e4434bdf6706.png
日期选择器(datepicker)https://www.zhihu.com/video/1081204916437139456

一.什么是日期选择器(datepicker)?

日期选择器在我们的网站或者其他应用上是经常碰到的,它具有让你快速选择日期的功能。日期选择器的类型也是各种各样的,但是总体上就和下图差不多:

07550b6f-7913-eb11-8da9-e4434bdf6706.png
日期选择器

0a550b6f-7913-eb11-8da9-e4434bdf6706.png
日期选择器

我们这次要做的就和上图差不多,记住最重要的一句话:实现过程才是最重要的,理解实现思路以及方法才是这篇文章的重点。

二.组件化开发思想

随着前端技术的发展,组件化开发的思想越来越深入人心,组件化并不是前端所特有的,一些其他的语言或者桌面程序等,都具有组件化的先例。其实总的来说,只要涉及到UI层面的开发,基本都会涉及到组件化开发思想。一个组件就是一个独立的个体,在网站开发中,一个页面可以由多个组件组成,比如说一个按钮可以是一个组件,一个侧边栏可以是一个组件,总之,随着前端技术的发展,组件化开发的思想是不可或缺的。

更多关于组件化可以阅读以下:

https://juejin.im/entry/582d3970d203090067f7de3d​juejin.im 关于前端组件化、状态管理规范化的思考 - 掘金​juejin.im 前端重构之路(组件化) - 前端 - 掘金​juejin.im

三.编写页面结构和样式

(一)HTML结构

我们可以看到HTML结构是很简单的,总共就分为了两大部分,一个head部分,一个body部分。这里值得注意的是我们给元素取的类名都比较长,比较特殊,这是因为我们这里是组件化开,所谓组件就是可以重复使用的,使用的场景有很多,所以我们就要尽可能的让我们的类名特殊,这样避免使用的使用重名。

<

此时没有样式的页面黑很丑,但是也能看出大体结构:

0e550b6f-7913-eb11-8da9-e4434bdf6706.png
没有样式

更过关于表格结构的知识点请移步:

HTML <table> 标签​www.w3school.com.cn

(二)添加样式

新增style.css文件

/* 最外层区域 */

样式部分没有什么可说的,都是用的很基本的样式属性,主要就是设置表头和表格的样式。补充一个我比较少接触到的样式属性:

CSS border-collapse 属性​www.w3school.com.cn

此时的日期选择器的基本结构已经完成:

10550b6f-7913-eb11-8da9-e4434bdf6706.png
基本结构完成

四.日历中的核心数据

所谓核心数据就是日历中显示每一天的数据,如下所示:

12550b6f-7913-eb11-8da9-e4434bdf6706.png
核心数据

这些数据的作用:

  • 渲染当月日历表格
  • 用户点击时获取日期值

五.需要事先了解的知识点

(一)日期对象——Data

W3C上的部分说明:

14550b6f-7913-eb11-8da9-e4434bdf6706.png
日期对象

具体详情请移步:

JavaScript Date(日期)对象​www.w3school.com.cn

这里我们主要是使用以下方式进行传值,因为当用户选择日期的时候,实际上是将点击的值传入Date对象里面,然后获取值:

new Date(year,month-1,date)//注意月份需要-1

注意:日期对象有一个“越界自动进(退)位”的特性。

(二)其他API——getFullYear()/getMonth()/getDate()/getDay()

W3C上的解释:

1.getFullYear()

15550b6f-7913-eb11-8da9-e4434bdf6706.png

19550b6f-7913-eb11-8da9-e4434bdf6706.png

详情请移步:

JavaScript getFullYear() 方法​www.w3school.com.cn

2.getMonth()

W3C上的解释:

1a550b6f-7913-eb11-8da9-e4434bdf6706.png

1b550b6f-7913-eb11-8da9-e4434bdf6706.png

具体详情请移步:

JavaScript getMonth() 方法​www.w3school.com.cn

3.getDate()

W3C上的解释:

1d550b6f-7913-eb11-8da9-e4434bdf6706.png

1f550b6f-7913-eb11-8da9-e4434bdf6706.png

详情请移步:

JavaScript getDate() 方法​www.w3school.com.cn

4.getDay()

W3C上的解释:

22550b6f-7913-eb11-8da9-e4434bdf6706.png

23550b6f-7913-eb11-8da9-e4434bdf6706.png

详情请移步:

JavaScript getDay() 方法​www.w3school.com.cn

(三)日期对象获取天数

这里为什么会把这个单独拿出来讲一下呢,那肯定是有它令人疑惑的地方:

  • 获取当月第一天:
new Date(year,month-1,1)
  • 获取当月最后一天
new Date(year,month,0)
  • 星期一-星期天
[1,2,3,4,5,6,0]//注意周天不是7而是0

这里我们可以看到获取当月最后一天的时候我们的月份并没有-1,那么就是默认获取的下一个月,然后我们天那里传的0,这里就解释了前面所说的“越界自动进(退)位”。

注意:我们传入的月份的范围:0~11

为什么会有这么奇怪的定义呢?打个比方,我们要获取某年2月份的最后一天,这时候很多人就可能会这样写:

new Date(year,1,28/29)//注意,因为month要-1,所以月份就要填1,才表示获取的2月份

这里大家应该就看得出来了,2月份有多少天是不固定的,所以我们传入值的时候就有可能不知道了,但是我们有了上面的规则就不一样了,我们可以这样写:

new Date(year,2,0)

利用“越界自动进(退)位”的特性,让系统自己去获取最后一天,这样是不是就不用我们瞎操心了,所以,任何事物存在必有它的道理的。

六.编写我们的JavaScript

(一)获取日历数据

我们新建一个data.js文件

(

index.html页面添加如下代码:

<script src="./data.js"></script>
<script>
    var monthDate = datepicker.getMonthDate(2019, 2);
    console.log(monthDate);
</script>

此时我们看一下打印台上打印的什么:

25550b6f-7913-eb11-8da9-e4434bdf6706.png

很明显,这里我们已经打印出来了2月份所有的天数,至于为什么会答应出来这么多天留给大家思考一下。

(二)数据渲染

获取到了数据那么重要的u是渲染到我们的日历当中了.

新建一个main.js

(function () {
    var datepicker = window.datepicker;
    datepicker.buildUi = function (year, month){
        var monthData = datepicker.getMonthDate(year, month);//获取一个月的数据
        //由于没有使用第三方插件,所以采用拼接字符串的方式
        var html = '<div class="ui-datepicker-header">'+
            '<a href="#" class="ui-datepicker-btn ui-datepicker-prev-btn">&lt;</a>'+
            '<a href="#" class="ui-datepicker-btn ui-datepicker-next-btn">&gt;</a>'+
            '<span class="ui-datepicker-curr-month">'+monthData.year+'-'+monthData.month+'</span>'+
        '</div>'+
        '<div class="ui-datepicker-body">'+
            '<table>'+
            '<thead>'+
                    '<tr>'+
                        '<th>一</th>'+
                        '<th>二</th>'+
                        '<th>三</th>'+
                        '<th>四</th>'+
                        '<th>五</th>'+
                        '<th>六</th>'+
                        '<th>日</th>'+
                    '</tr>'+
                '</thead>'+
                '<tbody>';
                for(var i=0; i<monthData.days.length; i++){
                    var date = monthData.days[i];
                    if(i%7 === 0){
                        html += '<tr>';
                    }
                    html += '<td>' + date.showDate + '</td>'
                    if(i%7 === 6){
                        html += '</tr>'
                    }
                }
                    '<tr>'+
                        '<td>29</td>'+
                        '<td>30</td>'+
                        '<td>1</td>'+
                        '<td>2</td>'+
                        '<td>3</td>'+
                        '<td>4</td>'+
                        '<td>5</td>'+
                    '</tr>'
                    html += '</tbody>'+
            '</table>'+
        '</div>';
        return html;
    };
    datepicker.init = function ($dom) {
        var html = datepicker.buildUi();
        $dom.innerHTML = html;
    }
})();

index.html页面加上:

<script>
    var monthDate = datepicker.getMonthDate(2019, 2);
    console.log(monthDate);

    datepicker.init(document.querySelector('.ui-datepicker-wrapper'))
</script>

可以看到我们的数据已经能够正常渲染出来了。值得注意的是我们在js里面进行了渲染,那么index.html里面的部分代码就可以不要了,变成这样:

<body>
    <div class="ui-datepicker-wrapper">
        
    </div>
</body>
<script src="./data.js"></script>
<script src="./main.js"></script>
<script>
    var monthDate = datepicker.getMonthDate(2019, 2);
    console.log(monthDate);

    datepicker.init(document.querySelector('.ui-datepicker-wrapper'))
</script>

27550b6f-7913-eb11-8da9-e4434bdf6706.png

注意:我们这里没有引用第三方插件或库,所以我们渲染的时候用的是字符串拼接,但是实践中通常采用的是第三方插件或库,更多知识可以参考:

前端模板与渲染方式 - axl234 - 博客园​www.cnblogs.com
29550b6f-7913-eb11-8da9-e4434bdf6706.png
JavaScript模板引擎Template.js使用详解 - 我竟然是凡人 - CSDN博客​blog.csdn.net JsRender/JsViews​www.jsviews.com JsRender 前端渲染模板基础学习 - 天空划落 - 博客园​www.cnblogs.com

(三)细节修改

我们都知道很多日期选择器的样式都是一个选择框,点击选择框然后才弹出日历,这里我们还没有实现,所以我们现在来改一下:

此时的index.html变成这样:

<html lang="en">
<head>
    <link rel="stylesheet" href="style.css">
    <style>
        .datepicker {
            border: 1px solid #ccc;
            border-radius: 4px;
            width: 230px;
            padding: 5px;
            line-height: 24px;
        }
        .datepicker:focus {
            outline: none;
            border: 1px solid #1abc9c;
        }
    </style>
</head>
<body>
    <input type="text" class="datepicker">   
</body>
<script src="./data.js"></script>
<script src="./main.js"></script>
<script>
    // var monthDate = datepicker.getMonthDate(2019, 2);
    // console.log(monthDate);
    // datepicker.init(document.querySelector('.ui-datepicker-wrapper'))
    datepicker.init('.datepicker');
</script>
</html>

然后修改一下main.js,动态的来创建我们的div:

    datepicker.init = function (input) {
        var html = datepicker.buildUi();
        // document.body.innerHTML = html;
        var $wrappper = document.createElement('div');
        $wrappper.className = 'ui-datepicker-wrapper';
        $wrappper.innerHTML = html;
        document.body.appendChild($wrappper);
    }

此时页面上多了一个文本框,但是此时我们的页面上已经没有了div了,整个包含日历的div元素由我们的js代码来创建。

2b550b6f-7913-eb11-8da9-e4434bdf6706.png

(四)日历的展开收起

我们可以看到一进页面的时候日历就已经存在了,一般情况下是需要点击input框的时候日历才会显示元素,而且我们需要采用定位的方式来对日历进行限定,因为页面上有其他元素,如果不采用定位的 话就会影响到其他元素。

在style.css里面添加一个类用来控制显示或者隐藏:

.ui-datepicker-wrapper {
    ........
    display: none;//添加默认隐藏
    position: absolute;//添加绝对定位
}
.ui-datepicker-wrapper-show {
    display: block;
}

此时我们在main.js里面的init函数里面设置显示或者隐藏,并且根据input框的位置动态的给日历添加top和left值,这样可以适用于多种场景,此时init函数变为:

    datepicker.init = function (input) {
        var html = datepicker.buildUi();
        // document.body.innerHTML = html;
        var $wrappper = document.createElement('div');
        $wrappper.className = 'ui-datepicker-wrapper';
        $wrappper.innerHTML = html;
        document.body.appendChild($wrappper);
        //控制显示或者隐藏
        var $input = document.querySelector(input);
        var isOpen = false;
        $input.addEventListener('click',function(){
            if(isOpen){
                $wrappper.classList.remove('ui-datepicker-wrapper-show');
                isOpen = false;
            }else {
                $wrappper.classList.add('ui-datepicker-wrapper-show');
               //获取input的位置,设置日历的位置
                var left = $input.offsetTop;
                var top = $input.offsetTop;
                var height = $input.offsetHeight;
                $wrappper.style.top = top + height + 2 + 'px';
                $wrappper.style.left = left + 'px';
                isOpen =true;
            }
        },false)
    }

这样编写之后,我们便能通过点击输入框实现日历的显示或者隐藏了,而且也能通过定位的方式来确定日历的位置。

2c550b6f-7913-eb11-8da9-e4434bdf6706.png
进入页面时候

点击之后:

2d550b6f-7913-eb11-8da9-e4434bdf6706.png
点击之后

(五)月份切换和日期选择

我们将实现月份切换的逻辑也放在init函数里面:

这里值得注意的是:我们的init函数只执行了一次,如果我们直接把点击事件绑定在btn上面,那么事件就只有在渲染页面的时候才会初始化一次,意味着只绑定了一次,但是在我们渲染之后,我们的按钮每一次都是根据html字符串重新渲染出来的,也就是我们的按钮会不断的销毁和重建,所以我们绑定的事件是无法生效的。所以我们这里采用将时间绑定在不变的外层元素wrapper上。

这里修改的地方较多,最终的main.js代码如下:

(

这里改动的地方较多,主要增加的是日期的权责和两个按钮事件,期间将一些逻辑单独提出来作为了方法,由于担心讲解的改动地方的时候讲漏,所以直接贴出main.js的最终代码。

四.总结

主要实现步骤:

  • 使用HTML合理规划组件结构
  • 为组件编写美观的样式
  • 如何使用Javascript获取组件所需要的数据
  • 将数据与HTML结构结合
  • 用户事件处理

到这里为止,日期选择组件就完成了,总的来说,实现过程较为复杂,但是只要你细心,只要你肯思考,一阵代码下来你肯定收获是非常多的。 另外,在我编写文章的时候有可能写漏或者写错,导致代码出现错误,特别是最后添加按钮点击事件和添加选择日期功能,这两步改动较多,所以笔者没有一一说明,容易导致错误或不理解,请不要卡壳,继续往下走,到后面你就会发现问题所在。此篇文章也是笔者边看慕课网上的视频自己边总结的,深刻理解其中的代码逻辑等。

源代码请移步:

Hacker233/JavaScript​github.com
30550b6f-7913-eb11-8da9-e4434bdf6706.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值