使用原生js实现一个简单的datePicker组件

2,476 阅读7分钟

我们今天要来实现一个简单的用原生 js 完成的一个最最基础的日历组件,废话不多说,直接开始

实现的效果


基础知识点

JavaScript Date(日期)对象实例

  • 返回当日的日期和时间

    var today = new Date();
    
    • 从 Date 对象返回一个月中的某一天 (1 ~ 31)
    today.getDate()
    
  • 从 Date 对象返回一周中的某一天 (0 ~ 6);注意:星期天是0

    today.getDay()
    
  • 从 Date 对象以四位数字返回年份

    today.getFullYear()
    
  • 从 Date 对象返回月份 (0 ~ 11)

    today.getMonth()
    
  • 返回 Date 对象的小时 (0 ~ 23)

    today.getHours()
    
  • 返回 Date 对象的分钟 (0 ~ 59)。

    today.getMinutes()
    
  • 返回 1970 年 1 月 1 日至今的毫秒数。

    today.getTime()
    
  • 返回某一个日期的Date对象

    // 比如我要拿到5月27号的Date对象
    
    var aDay = new Date('2018-05-27')
    
    // 或者
    var aDay = new Date(2018,4,27) // 月份是从0开始的
    

更多可以参考 JavaScript Date 对象;


页面结构

这里我们就不细讲了,我们会在第五节讲动态渲染日历数据。


获取日历每月显示的数据

首先我们要实现一个函数,这个函数的作用就是拿到我们这个 datePicker 需要渲染的数据。

我们实现的六行七列的数据,如下图:

我们首先创建一个 data.js ,同时在里面定义一个 getMonthDate 的方法,在 ret 存入我们所要渲染的天数。

(function(){
  var datepicker = {};
  datepicker.getMonthDate = function(year ,month) {	
    var ret = [];
    return {
      days: ret
    };
 }

 window.datepicker = datepicker;
})();

这个函数需要接受的参数是年份与月份,如果不传的话,则默认传入当前的月份与年份。

如下:

if(!year && !month){
  // 获取当天日期对象
  var today =  new  Date();
  // 获取当前年份
  year = today.getFullYear();
  // 获取当前月份
  month = today.getMonth() + 1;
}

这里面还有一个比较重要的点就是,就是我们怎么显示上一个月的值,以及他们分别是几号。

这里我们只要获取到这个月第一天是星期几就可以了,然后前面的就是相应显示上个月的值。

// 这个月第一天的Date对象
var firstDay = new Date(year ,month-1, 1);
// 那个这个月第一天具体是星期几
var firstDayWeekDay = firstDay.getDay();
// 0的话就是星期天
if(firstDayWeekDay === 0)firstDayWeekDay = 7; 
// 拿到当前的年份
year = firstDay.getFullYear();
// 拿到当前的月份
month = firstDay.getMonth() + 1;

我们还需要拿到上个月和这个月的最后一天,便于我们稍后组装数据。我们可以用这个月的第0天得到上个月的最后一天。

// 上个月的最后一天
var lastDayofLastMonth = new Date(year, month-1, 0);
// 上个月的具体日期
var lastDateofLastMonth = lastDayofLastMonth.getDate();
// 上个月在第一行要显示几天
var preMonthDayCount = firstDayWeekDay-1;
// 这个月的最后一天
var lastDay = new Date(year,month,0);
// 这个月的最后一天具体日期
var lastData = lastDay.getDate();

接下去我们就需要去拼装数据了。

遍历数据:

数据结构的图我刚刚已经发过了,我们这边需要循环遍历这六行七列的42个数据,而且每一个数据包括

{
  date: 代表我们时当月的第几天,其实是一个中介,为了计算showDate
  month: 代表我们是第几个月
  showDate: 代表我们当前天数是确切的第几天
}

遍历的代码如下:

for (var i = 0; i < 6*7; i++){
  // 赋值date的值,这里上个月的最后一天为0
  var date = i+1-preMonthDayCount;
  // 赋值showDate,上下月份,下面再做判断
  var showDate = date;

  // 赋值月份
  var thisMonth = month;

  // 当date < 0时,则代表是上一个月
  if (date <= 0){
    thisMonth = month-1; 	// 月份减一
    showDate = lastDateofLastMonth + date; 	// 显示上一个相应是几号
  } else if (date > lastData){
    // 当date大于了这个月最后一天,那么代表下个月
    thisMonth = month+1; 	// 月份加一
    showDate = showDate - lastData; 		// 显示下一个月具体几号
  }

  // 当我们月份是13的时候,代表下一年,月份置为一
  if(thisMonth === 13) thisMonth = 1;

  // 当我们月份是0的时候,代表上一年,月份置为一
  if(thisMonth === 0)thisMonth = 12;

  // 最后塞入到我们的ret中去
  ret.push({
    date: date,
    month: thisMonth,
    showDate: showDate,
  });
}

经过以上代码的处理,我们便能得到最初图中所示的数据了。


开始动态渲染数据

我们在建一个 datePicker.js ,我们在这个 js 中做一些页面渲染的工作。

首先我们要来思考一下我们要做一些什么工作,一般在下手开始敲代码之前,我们先要想好思路,然后在开始工作。

最开始,我们肯定要获取到日历数据,然后将数据动态填充到我们准备好的模版里面去。

然后我们要实现一些事件,改变月份,然后进行相应的的月份数据;点击相应的天数,使日历组件消失,input框中显示选择的天数。

这样我们可以写一个代码的大致框架出来:

(function(){
  var datepicker = window.datepicker;

  // 日历组件的函数
  datepicker.buildUI = function(year ,month){ 
    var html = datepicker.buildUI(year,month);
    return html;
  };

  // 页面的render函数
  datepicker.render = function(direction){ 
    var year,month;
    var html = datepicker.buildUI(year,month);
    $wrapper = document.querySelector('.ui-datePicker-wrapper');
    $wrapper.innerHTML = html;
  }

   // 页面的入口函数
  datepicker.init = function(input){
  datepicker.render();
  }

  // 一个工具函数,将Date对象转化成YYYY-MM-DD的格式
  function format(date){
    var ret = '';
    var padding =  function(num) {
      if (num <= 9) {
        return  '0' + num;
      } else {
        return num;
     }
    }

    ret += date.getFullYear() + '-';
    ret += padding(date.getMonth() + 1) + '-';
    ret += padding(date.getDate());

    return  ret;
  }
})();

然后我们在 init 函数中添加一些事件函数,如下图:

$input.addEventListener('click', function(){
  if (isOpen) {
    $wrapper.classList.remove('ui-datePicker-wrapper-show');
    isOpen = false;
  } else {
    $wrapper.classList.add('ui-datePicker-wrapper-show');
    var left = $input.offsetLeft;
    var top = $input.offsetTop;
    var height = $input.offsetHeight;

    $wrapper.style.top = top + height + 8 + 'px';
    $wrapper.style.left = left + 2 +'px';

    isOpen = true;
  }
}, false);
		
$wrapper.addEventListener('click', function(e){
  var $target = e.target;

  if (!$target.classList.contains('ui-datePicker-btn')) return;
  if ($target.classList.contains('ui-datePicker-prev-btn')) {

  datepicker.render('prev');

  } else if ($target.classList.contains('ui-datePicker-next-btn')) {
    datepicker.render('next');
  }
}, false);

$wrapper.addEventListener('click', function(e){
  var $target = e.target;
  if($target.tagName.toLowerCase() !=='td') return;

  var date = new Date(monthDate.year,monthDate.month-1, $target.dataset.date);

  $input.value = format(date);
  $wrapper.classList.remove('ui-datePicker-wrapper-show');
  isOpen = false;
}, false);

接下来我们主要看看 buildUI 的函数实现,这里其实就是实现一个动态的html,然后渲染到页面中而已,就跟我们平时从 Ajax 渲染数据一样。

datepicker.buildUI = function(year ,month){
  monthDate = 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">'+ monthDate.year + '-' + monthDate.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<monthDate.days.length;i++){
		        var date = monthDate.days[i];
								
		        if (i%7 === 0) {
		          html += '<tr>';
		        }

		        if (date.date <= 0) {
		          html += '<td data-date = ' + 
		              date.date + ' class = "notThisMonth">'+
		              date.showDate +'</td>';
		        } else if (date.date > date.showDate) {
		          html += '<td data-date = ' +
		              date.date+' class = "notThisMonth">' +
		              date.showDate +'</td>'
		        } else if(
		          date.showDate == today.getDate();
		          && monthDate.month == today.getMonth() + 1;
		          && monthDate.year == today.getFullYear();
		        ) {
		          html += '<td data-date = ' +
		            date.date+' class = "on">'+
		            date.showDate +'</td>';
		        } else {
		          html += '<td data-date = ' +
		            date.date+'>' +
		            date.showDate +'</td>';
		        }

		        if(i%7 === 6){
		          html += '</tr>';
		        }
		      }
 
          html += '</tbody>'+
        '</table>'+
      '</div>';
      return html;
	};

最后我们看看 index.html 的文件内容怎么样,如下图:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8" />
  <title>DatePicker</title>
  <link rel="stylesheet" type="text/css" href="css/datePicker.css"/>
  <style type="text/css">
    .datepicker {
      height: 24px;
      line-height: 24px;
      width: 250px;
      padding: 5px;
      border: 1px solid #CCCCCC;
      border-radius: 5px;
    }

    .datepicker:focus {
      outline: 0 none;
      border: 1px solid #1ABC9C;
    }

    h3 {
      color: #1ABC9C;
    }

    h3, input {
      position: absolute;
      left: 30%;
    }

    input {
      top: 60px;
    }
  </style>
  </head>
  <body>
    <h3>请选择日期</h3>
    <input type="text" class="datepicker" />

    <script src="js/date.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/datePicker.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
      datepicker.init('.datepicker');
    </script>
  </body>
</html>

页面引入两个 js,并向外暴露一个变量对象 datepicker ,然后调用相应的 init 方法。


总结

至此我们便完成了一个简单的用原生js完成的一个最最基础的日历组件,其中里面最主要的就是获取日期数据的这个函数,实现的思路其实也很常规,其他的关于渲染的流程,其实我们平时工作中也会经常碰到。

感谢课程 datePicker组件开发课程,记录了自己的一些思考和总结。

第一篇文章,望大佬们轻喷😋😋😋