ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

bootstrap-datetimepicker源代码分析

2022-06-04 13:03:38  阅读:209  来源: 互联网

标签:源代码 bootstrap 点击 input datetimepicker border 弹窗 页面


bootstrap-datetimepicker日期插件应用非常流行,如果对其源代码一无所知,在遇到意外问题时可能就有麻烦。

本人在应用datetimepicker时曾经遇到百思不得其解的问题,经历几天的源代码分析和测试,才最终得知原因和解决办法,闹半天,这么高大上的插件也是有

bug有坑的,这也体现了前端的复杂性,后台java再复杂也没有这种事件复杂性。

本文就是分享一下本人对源代码分析的结果。

 

1)首先,写一下datetimepicker在页面引入使用的常规简单方法:

<link rel="stylesheet" href="../css/bootstrap.min.css">
<link rel="stylesheet" href="../css/bootstrap-datetimepicker.min.css">
<script src="jquery.js" ></script>
<script src="bootstrap-datetimepicker.js" ></script>
</head>
<body>
<input type="text" id="datetimepicker" class="form-control" readonly />
<script>
$( "#datetimepicker" ).datetimepicker({
format: 'yyyy-mm-dd',//显示格式
startView:2, // 选day(年月日)
minView:2,
language: 'zh-CN',
autoclose: 1,//选择后自动关闭
clearBtn:true//清除按钮
});

view子页面0-4别对应分时日月年,比如view=2对应day(日)。

format参数决定在input显示哪些部分,比如'yyyy-mm-dd'则不显示时分。
以选取年月日为例,如果maxView=2,则点击年不会选年,所以不能写maxView:2。

datetimepicker弹窗层自己写了css,与bootstrap.css无关。

2)datetimepicker的入口函数代码


$("input").datetimepicker(options)入口函数代码:
$.fn.datetimepicker = function (option) {
...
this.each(function () {
var $this = $(this),
data = $this.data('datetimepicker'),
if (!data) {
$this.data('datetimepicker', (data = new Datetimepicker(this, $.extend({}, $.fn.datetimepicker.defaults, options))));
}
...
};

它是遍历$("input")的每一个input元素,new Datetimepicker() 一个实例,保存在input元素的data属性中。
var Datetimepicker = function (element, options) {
此即为new实例的构造函数,相当于java的主类,这里先略过。

如果页面有多个input,则多实例,每个input执行一次new Datetimepicker(element,options)实例,创建一个DOM插入页面:
<div class="datetimepicker datetimepicker-dropdown-bottom-right dropdown-menu"

页面中这些容器没有区别,它们对应js的不同对象(jquery对象)保存在不同的实例中。

每个实例对应页面中的一个input和<div>,<div>通过内部对象索引对应js对象实例,多实例相互之间无关系。

$()无论是新建html元素插入页面还是从页面选取html元素,都是与页面元素对应,对这个对象进行操作就是对页面元素进行操作,其内部原理就是html/DOM技术,app处理界面其实与html是类似的。

多实例举例:
var obj1 = $('<div>hello</div');
var obj2 = $('<div>hello</div');
$().append(obj1)
$().append(obj2)

看页面是没法区分这两个<div>的,但在js它们对应不同的jquery对象,实际上就是对应不同的DOM对象,obj1对应第一个<div>,obj2对应第二个<div>,
js代码可以对任意一个<div>进行操作,比如obj1.remove()就从页面把第一个<div>删除了,obj1把自身删除了。

 

3)datetimepicker的主类

下面来看主类:
var Datetimepicker = function (element, options) { // element是input元素,options是传入的参数{}
this.xxx = xxx; // 接受传入的参数,设置一些属性保存在实例中,不再一一细说,自己看
...
其中:
this.picker = $(template)
.appendTo(this.isInline ? this.element : this.container) // 'body'
.on({
click: $.proxy(this.click, this),
mousedown: $.proxy(this.mousedown, this)
});

这是构造弹窗层template/table并插入页面之后绑定点击事件,构造template比较复杂,分年月日时分好几块。
一般来说弹窗层插入body,在body页面中定位,但也可以插入div容器中,它有个判断。

 

input绑定点击事件的代码:
Datetimepicker.prototype = {
constructor: Datetimepicker,

_events: [],
_attachEvents: function () {
this._detachEvents();
if (this.isInput) { // single input
this._events = [
[this.element, {
focus: $.proxy(this.show, this),
keyup: $.proxy(this.update, this),
keydown: $.proxy(this.keydown, this)
}]
];
}else...

for (var i = 0, el, ev; i < this._events.length; i++) {
el = this._events[i][0];
ev = this._events[i][1];
el.on(ev);
}

一般事件绑定的函数直接写函数,函数中可以引用全局对象,如果函数涉及所在的对象,可以写成:
$.proxy(this.show, this),
有点类似bind的意思,变换/指定函数所在的对象。

注意它是绑定input的focus事件,这是有bug有坑的,而且复杂深奥不好debug找原因,踩过hover和blur坑的人可能能猜到会出什么坑,
我这里只提示一下,与浮动块(弹窗层)有关,就不细说了。

下面是document绑定"点击"事件:

$(document).on('mousedown', function (e) {
// Clicked outside the datetimepicker, hide it
if ($(e.target).closest('.datetimepicker').length === 0 ) {
that.hide();
}
});

当点击页面时,也就是当点击的区域不是弹窗层时,关闭日期选取弹窗层。

看了上述鼠标事件绑定之后,我们知道,当点击input时,如果有focus事件则触发执行this,show()方法显示弹窗,很简单。

this.show()方法就是简单设置display=block显示弹窗层,并且执行e.stopPropagation()阻止鼠标事件冒泡到document,否则鼠标事件冒泡到document

会执行事件处理函数关闭弹窗。

 如果鼠标在input反复点击,由于focus事件要鼠标离开input再点击input才有,所以没有重复的focus事件,就没有反应。由于input不做为输入,而是做为

一个按钮,反复点击应该有toggle效果,所以这样用户体验是不好的,严格说是不能通过测试的,可以自己修改代码解决这个问题,具体怎么改,只有知道

这个问题,相信都有这个能力,比如可以在document点击事件处理函数加判断当判断鼠标事件target是input时,就toggle弹窗层显示,而不是仅仅简单

判断如果点击的区域不是弹窗层就关闭弹窗层,这是这个插件的败笔所在。

 

大家还记得input是绑定focus,document是绑定mousedown,考虑到弹窗层的向上小三角与input略有重叠,这是有bug有坑的,

会出现奇怪的现象,体现了前端事件的复杂性,这个插件的作者可能没有意识到这个问题(大意了)。


一些细节分析:

选取天会更新this.date会setValue()更新input的值,如果执行hide(),也会更新input的值。

每次点击都要更新this.date日期值,因为可以点击选取上个月末尾几天或者下个月头几天,计算年月日还是挺复杂的,
再根据date重新构造年月日时分列表。


Datetimepicker是核心类,fill是核心数据处理代码,每次点击因为日期数据发生变化因此都要执行更新代码,更新弹窗页面内容,点击更新处理非常复杂,
属于复杂数据和页面处理。


fill: function(){

构造弹窗层里面的html内容,每个月的day列表,table形式显示,类似日历显示,数据算法处理比较复杂。
while循环产生每一天的html代码,最后拼接插入tbody。
for循环产生小时/分列表。
用Date.getUTCxxx和setUTCxxx方法,类似java Calendar。

place()
弹窗插入页面body,在页面定位top = input top + input高度。


datepicker年月日数据算法
isLeapYear: function (year) {
return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
},
getDaysInMonth: function (year, month) {
return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
},

bootstrap-datetimepicker.xxx.js是客户化语言包,中文语言包是zh-CN:
$.fn.datetimepicker.dates['zh-CN'] = {
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
today: "今天",
suffix: [],
meridiem: ["上午", "下午"]
};

另外,比如这种代码:
this.element.trigger({
type: 'changeDay',
date: this.viewDate
});
这是自定义事件,并没有绑定这个自定义事件,所以没有用。

datetimepicker弹窗层上边框上箭头实现方法:
.datetimepicker-dropdown-bottom-right:before {
top: -7px;
left: 6px;
}
[class*=" datetimepicker-dropdown"]:before {
content: '';
display: inline-block;
border-left: 7px solid transparent; // 向上三角,背景色是灰色
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-bottom-color: rgba(0,0,0,0.2);
position: absolute;
}
*, *:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.datetimepicker-dropdown-bottom-right:after {
top: -6px;
left: 7px;
}
[class*=" datetimepicker-dropdown"]:after {
content: '';
display: inline-block;
border-left: 6px solid transparent; // 向上三角,背景色是白色,与向上灰色三角重叠覆盖住再往下偏移一点
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
position: absolute;
}

它是before/after两个重叠的三角(上箭头),一个灰色,一个白色,白色箭头块略向下错开一点,效果就显示出灰色的上箭头,由于是浮动块,会遮挡父元素的边框线。

 

本文到此结束,本人水平有限,文中不妥之处欢迎大家指正。

 

标签:源代码,bootstrap,点击,input,datetimepicker,border,弹窗,页面
来源: https://www.cnblogs.com/pzhu1/p/16341670.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有