最近在使用 Jonathan Peterson 所写的 bootstrap-datetimepicker 这一插件,然而由于网页设计的一些问题,无法在静态元素上正常使用该插件,蹦出自己尝试修改源代码的想法。说干就干,还竟然成功了,现在再完整地研究一下该插件的源代码!
bootstrap-datetimepicker 源代码(version:4.17.45)一共2725行,总的来说,分为两个部分
第一个函数用来检查是否使用了AMD规范,以及所需要的 jQuery 和 moment.js 是否已经加载,最后调用factory。这个函数比较简单,代码如下:
function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// AMD is used - Register as an anonymous module.
define(['jquery', 'moment'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('jquery'), require('moment'));
} else {
// Neither AMD nor CommonJS used. Use global variables.
if (typeof jQuery === 'undefined') {
throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
}
if (typeof moment === 'undefined') {
throw 'bootstrap-datetimepicker requires Moment.js to be loaded first';
}
factory(jQuery, moment);
}
}
重点来了,让我们仔细观察第二个函数的内容,简化为三个部分
function ($, moment) {
'use strict';
if (!moment) {
throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first');
}
// Part one
var dateTimePicker = function (element, options) {
...};
// Part two
$.fn.datetimepicker = function (options) {
...};
// Part three
$.fn.datetimepicker.defaults = {
...};
return $.fn.datetimepicker;
}
整体上,这个函数首先声明一个名为 dateTimePicker 的 JavaScript 对象,将 datetimepicker 这个方法拓展为 $.fn
的方法,接着给 datetimepicker 一个名为 defaults 的属性赋值,最后返回 $.fn.datetimepicker
接下来,我们对每一部分的代码进行解析
首先,让我们来看一下第一部分,这一部分的主要工作是返回一个对象,接受的参数是element以及options,部分代码如下:
var dateTimePicker = function (element, options) {
var picker = {},
.
.
.
return picker;
}
那么这两个参数到底接受的是什么内容呢?在第二部分的代码中我们会找到答案,现在就一起先睹为快:
$.fn.datetimepicker = function (options) {
options = options || {};
var args = Array.prototype.slice.call(arguments, 1),
isInstance = true,
thisMethods = ['destroy', 'hide', 'show', 'toggle'],
returnValue;
if (typeof options === 'object') {
return this.each(function () {
var $this = $(this),
_options;
if (!$this.data('DateTimePicker')) {
// create a private copy of the defaults object
_options = $.extend(true, {}, $.fn.datetimepicker.defaults, options);
$this.data('DateTimePicker', dateTimePicker($this, _options));
}
}); // 剩余部分省略
关键在于 dateTimePicker($this, _options)
,那么这个 $this
又是何方圣神呢?继续向上溯源,发现var $this = $(this)
,也就是说,这个$this
代表的就是调用 datetimepicker 的HTML元素所对应的 jQuery 对象,而这个 options 则是一个 JavaScript 对象,这个对象是由用户在使用插件时添加的一些选项所组成的。
明白这个函数的最终功能之后,我们看具体的代码。
首先定义了许多局部变量、函数以及 picker 的各种方法,这些方法比较简单,这里就不再赘述。
先来看具体的执行过程,主要代码如下:
// initializing element and component attributes
if (element.is('input')) {
input = element;
} else {
input = element.find(options.datepickerInput);
if (input.length === 0) {
input = element.find('input');
} else if (!input.is('input')) {
throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element');
}
}
if (element.hasClass('input-group')) {
// in case there is more then one 'input-group-addon' Issue #48
if (element.find('.datepickerbutton').length === 0) {
component = element.find('.input-group-addon');
} else {
component = element.find('.datepickerbutton');
}
}