重构日期选择级联组件
2011-03-01 10:04
351 查看
这是一个很常见的日期选择级联组件,先看下效果:
View Code
这个组件是基于Cubee里的相同组件Y.DateCascade改写的,由于原来的组件代码依赖于YUI过于繁琐及存在着一些bug,于是用原生js重构了一遍。以下是重构的过程的一些思路。
日期需要有一个上限及下限,比如从1900年1月1日开始,最多能选到2012年12月21日;
能根据不同的月份显示不同的天数,比如1月有31天可选,闰年的2月则只有29天可选等;
可以手动设定其默认选中的值;
最后一个特殊的地方,依赖一个input的文本框(业务需要),当JS失效时可以实现平稳退化,手动输入日期(当然可以不依赖这个,直接将select的功能抽取出来)。
dateStart:日期的下限,可选,默认为1900/01/01;
dateEnd:日期的上限,可选,默认为当天;
dateDefault:初始化时默认选中的日期,可选,可以为input里的日期值;
id:input的id,js将会将select动态加到input节点的后面。
组件的使用方法如下:
render:渲染结构,这个方法在初始化init的时候调用了一次,后面可以多次调用更改其参数重新渲染结构(不影响之前绑定过的事件);
renderYear,renderMonth,renderDay:分别渲染年月日的select结构;
setNewDate:设置新的选中日期,参数形式有两种:
setNewYear,setNewMonth,setNewDay:分别设置新的年月日;
所以起始点在于获取年月日的可选择范围:
然后粗略浏览一下实现整个组件的逻辑方法:(细节请看这里)
render年月日结构时使用原生的select.add(new Option(text, value), undefined)的形式添加选项,速度快(原来的组件使用YUI方法添加的,IE6下会出现渲染延迟的bug);
将日期值转成数值时要用parseInt(v, 10),别忘了后面的参数10,因为如果v是以0开头的数最后会转成八进制数,而日期经常会出现以0开头的数;
选中值的时候可以直接使用select.value = v的形式,而不用一个个option的匹配;
用3的方法选值,如果选项里没有对应的值,不同浏览器的处理不一样,除IE外的浏览器会默认选中第一个选项,而IE不会选中任何值,因此要hack一下;
添加了getSelectedDate(取得选中日期的值)和updateInput(更新input里的值)的方法;
没有设置回调函数的功能(个人觉得没必要)
// /**
* DateCascade 日期级联组件
* @author 虎牙 huya.nzb@taobao.com/ningzbruc@gmail.com
* @date 2011-01-31
*/
var DateCascade = function(){
/**
* 日期级联组件模块
* @module datecascade
* @requires base, node
*/
/**
* 公共函数
*/
function $(id){
return (id) && (typeof id === 'string' ? document.getElementById(id) : id);
}
function addEvent(elem, event, foo, capture){
if (elem.addEventListener) {
elem.addEventListener(event, foo, !!capture);
} else if (elem.attachEvent) {
elem.attachEvent('on' + event, foo);
} else {
elem['on' + event] = foo;
}
}
function removeEvent(elem, event, foo, capture){
if (elem.removeEventListener) {
elem.removeEventListener(event, foo, !!capture);
} else if (elem.detachEvent) {
elem.detachEvent('on' + event, foo);
} else {
elem['on' + event] = null;
}
}
function insertAfter(node, rel){
var parent = rel.parentNode;
if (parent.lastChild == rel) {
parent.appendChild(node);
}
else {
parent.insertBefore(node, rel.nextSibling);
}
}
/**
* 日期级联组件
* @class DateCascade
* @constructor
*/
function DateCascade(){
this._init.apply(this, arguments);
}
DateCascade.prototype = {
/**
* 构造函数属性
* @attribute
*/
constructor: DateCascade,
/**
* 初始化
* @method _init
* @param {String} id 相关的input的id
* @param {Object} config 配置参数
* @private
*/
_init: function(id, config){
var that = this, doc = document;
if (!(that._input = $(id))) {
return;
}
that._input.style.display = 'none';
that._Y = doc.createElement('select');
that._Y.id = id + '_selectyear';
insertAfter(that._Y, that._input);
that._M = doc.createElement('select');
that._M.id = id + '_selectmonth';
insertAfter(that._M, that._Y);
that._D = doc.createElement('select');
that._D.id = id + '_selectday';
insertAfter(that._D, that._M);
that._dayInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
that.render(config);
that._bindEvent();
},
/**
* 构造参数
* @method _buildParam
* @param {Object} config 配置参数
* @private
*/
_buildParam: function(config){
config = config || {};
var that = this, _df = that._input.value;
that.dateStart = (config.dateStart && that._checkDate(config.dateStart)) || that.dateStart || new Date('1900/01/01');
that.dateEnd = (config.dateEnd && that._checkDate(config.dateEnd)) || that.dateEnd || new Date();
that.dateDefault = config.dateDefault && that._checkDate(config.dateDefault) || that.dateDefault || (_df && that._checkDate(_df));
},
/**
* 绑定事件
* @method _bindEvent
* @private
*/
_bindEvent: function(){
var that = this;
addEvent(that._Y, 'change', function(e){
that.setNewMonth(parseInt(that._M.value, 10));
});
addEvent(that._M, 'change', function(e){
that.setNewDay(parseInt(that._D.value, 10));
});
},
/**
* 渲染结构
* @method render
* @param {Object} 配置参数
* @chainable
*/
render: function(config){
var that = this;
that._buildParam(config);
var _ds = that.dateStart, _de = that.dateEnd, _df = that.dateDefault, _y = that._Y.value, _m = that._M.value, _d = that._D.value;
that._dsYear = _ds.getFullYear();
that._dsMonth = _ds.getMonth() + 1;
that._dsDay = _ds.getDate();
that._deYear = _de.getFullYear();
that._deMonth = _de.getMonth() + 1;
that._deDay = _de.getDate();
if (_df) {
that.setNewDate(_df);
} else if (_y || _m || _d) {
that.setNewYear(_y, false).setNewMonth(_m, false).setNewDay(_d);
} else {
that.renderYear().renderMonth().renderDay();
that._Y.options[0].selected = true;
that._M.options[0].selected = true;
that._D.options[0].selected = true;
}
return this;
},
/**
* 检查日期是否在给定的区间内
* @method _inDateRange
* @param {Date} date
* @private
* @return {Boolean}
*/
_inDateRange: function(date){
var that = this, date = that._checkDate(date);
if (!date) {
return;
}
var start = that._dsYear * 10000 + that._dsMonth * 100 + that._dsDay, end = that._deYear * 10000 + that._deMonth * 100 + that._deDay, date = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate();
if (date < start || date > end) {
return false;
}
return true;
},
/**
* 是否是闰年
* @method _isLeapYear
* @param {String | Number} y
* @private
* @return {Boolean}
*/
_isLeapYear: function(y){
y = parseInt(y, 10);
return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0);
},
/**
* 是否是Date格式
* @method _isDate
* @param {Date} d
* @private
* @return {Boolean}
*/
_isDate: function(d){
return Object.prototype.toString.call(d) === '[object Date]' && d.toString() !== 'Invalid Date' && !isNaN(d);
},
/**
* 检查是否是正确的Date格式
* @method _checkDate
* @param {Date} d
* @private
* @return {Date | Boolean} 如果正确则返回Date,否则返回false
*/
_checkDate: function(date){
return date && ((this._isDate(date) && date) || (this._isDate(new Date(date)) && new Date(date)));
},
/**
* 取得当前选中的日期
* @method getSelectedDate
* @return {String} 返回格式如 2011-02-28
*/
getSelectedDate: function(){
var that = this, y = that._Y.value, m = that._M.value, d = that._D.value;
if (!y || !m || !d) {
return '';
}
return y + '-' + m + '-' + d;
},
/**
* 更新隐藏的input的值
* @method updateInput
* @chainable
*/
updateInput: function(){
var v = this.getSelectedDate();
v && (this._input.value = v);
return this;
},
/**
* 设置新日期
* @method setNewDate
* @param {Date} date optional
* @param {String | Number} y optional
* @param {String | Number} m optional
* @param {String | Number} d optional
* @chainable
*/
setNewDate: function(/*y, m, d*/) {
var that = this, args = arguments;
if (args.length === 3) {
var y = args[0], m = args[1], d = args[2];
} else if (args.length === 1) {
var date = that._checkDate(args[0]);
if (!date) { return this; }
var y = date.getFullYear(), m = date.getMonth() + 1, d = date.getDate();
} else {
return this;
}
that.setNewYear(y, false).setNewMonth(m, false).setNewDay(d);
return this;
},
/**
* 设置新年份
* @method setNewYear
* @chainable
* @param y {String}
* @param l {Boolean} linkage 改变年份时是否联动月份及天数 默认为true
*/
setNewYear: function(y, l){
var that = this, m = parseInt(that._M.value, 10), d = parseInt(that._D.value, 10);
that.renderYear();
that._Y.value = y;
//IE Bugfix, 上一行代码在下拉框没有相对应的值的时候,除IE外的浏览器默认选中第一个选项
//IE不选中任何选项,默认为空
if (that._Y.selectedIndex < 0) {
that._Y.options[0].selected = true;
}
if (l === false) {
//更新input的值
that.updateInput();
return this;
}
that.setNewMonth(m);
return this;
},
/**
* 设置新月份
* @method setNewMonth
* @chainable
* @param m {String}
* @param l {Boolean} linkage 改变年份时是否联动天数 默认为true
*/
setNewMonth: function(m, l){
var that = this, y = parseInt(that._Y.value, 10), d = parseInt(that._D.value, 10), m = (parseInt(m, 10) < 10 ? '0' : '') + parseInt(m, 10);
that.renderMonth(y);
that._M.value = m;
if (that._M.selectedIndex < 0) {
that._M.options[0].selected = true;
}
if (l === false) {
that.updateInput();
return this;
}
that.setNewDay(d);
return this;
},
/**
* 设置新天数
* @method setNewDay
* @chainable
* @param d {String}
*/
setNewDay: function(d){
var that = this, y = parseInt(that._Y.value, 10), m = parseInt(that._M.value, 10), d = (parseInt(d, 10) < 10 ? '0' : '') + parseInt(d, 10);
that.renderDay(y, m);
that._D.value = d;
if (that._D.selectedIndex < 0) {
that._D.options[0].selected = true;
}
that.updateInput();
return this;
},
/**
* 渲染年份结构
* @method renderYear
* @chainable
*/
renderYear: function(){
var that = this, r = that._getYearRange(), options = that._Y.options, l = options.length, i, newOption;
//年份是从大到小排列,如果不变,则不进行渲染
if (l > 1 && options[1].value == r.max && options[l - 1].value == r.min) {
return this;
}
that._Y.innerHTML = '';
that._Y.add(new Option('-', ''), undefined);
for (i = r.max; i >= r.min; i--) {
newOption = new Option(i, i);
that._Y.add(newOption, undefined);
}
return this;
},
/**
* 渲染月份结构
* @method renderMonth
* @param {String | Number} 当前年份
* @chainable
*/
renderMonth: function(y){
var that = this, r = that._getMonthRange(y), options = that._M.options, l = options.length, i, newOption, t;
if (l > 1 && options[1].value == r.min && options[l - 1].value == r.max) {
return this;
}
that._M.innerHTML = '';
that._M.add(new Option('-', ''), undefined);
for (i = r.min; i <= r.max; i++) {
t = i >= 10 ? i : '0' + i;
newOption = new Option(t, t);
that._M.add(newOption, undefined);
}
return this;
},
/**
* 渲染天数结构
* @method renderDay
* @param {String | Number} y 当前年份
* @param {String | Number} m 当前月份,不以0开头的十进制数
* @chainable
*/
renderDay: function(y, m){
var that = this, r = that._getDayRange(y, m), options = that._D.options, l = options.length, i, newOption, t;
if (l > 1 && options[1].value == r.min && options[l - 1].value == r.max) {
return this;
}
that._D.innerHTML = '';
that._D.add(new Option('-', ''), undefined);
for (i = r.min; i <= r.max; i++) {
t = i >= 10 ? i : '0' + i;
newOption = new Option(t, t);
that._D.add(newOption, undefined);
}
return this;
},
/**
* 获取年份区间
* @method _getYearRange
* @return {Object}
* @private
*/
_getYearRange: function(){
var that = this;
return {
min: that._dsYear,
max: that._deYear
};
},
/**
* 获取月份区间
* @method _getMonthRange
* @param y {Number}
* @return {Object}
* @private
*/
_getMonthRange: function(y){
var that = this, min = 1, max = 12;
if (y == that._dsYear) {
min = that._dsMonth;
}
if (y == that._deYear) {
max = that._deMonth;
}
return {
min: min,
max: max
};
},
/**
* 获取天数区间
* @method _getMonthRange
* @param y {Number}
* @param m {Number}
* @return {Object}
* @private
*/
_getDayRange: function(y, m){
var that = this, min = 1, max = 31;
if (m) {
if (m != 2) {
max = that._dayInMonth[m - 1];
}
else {
max = that._isLeapYear(y) ? 29 : 28;
}
if (y == that._dsYear && m == that._dsMonth) {
min = that._dsDay;
}
if (y == that._deYear && m == that._deMonth) {
max = that._deDay;
}
}
return {
min: min,
max: max
};
}
};
return DateCascade;
}();
// ]]>
// window.onload = function() {
var date = new DateCascade('birthday');
};
// ]]>
View Code
<div> <p> <input type="text" id="birthday" name="birthday" value="2008/2/29" /> </p> </div> <script src="http://files.cnblogs.com/akakingback/datacascade-origin.js"></script> <script> window.onload = function() { var date = new DateCascade('birthday'); }; </script>
这个组件是基于Cubee里的相同组件Y.DateCascade改写的,由于原来的组件代码依赖于YUI过于繁琐及存在着一些bug,于是用原生js重构了一遍。以下是重构的过程的一些思路。
一、分析组件的结构及行为
其实这个组件的结构及行为很简单,不外乎是年月日间互相联动,动态改变select里的option个数及其值。日期需要有一个上限及下限,比如从1900年1月1日开始,最多能选到2012年12月21日;
能根据不同的月份显示不同的天数,比如1月有31天可选,闰年的2月则只有29天可选等;
可以手动设定其默认选中的值;
最后一个特殊的地方,依赖一个input的文本框(业务需要),当JS失效时可以实现平稳退化,手动输入日期(当然可以不依赖这个,直接将select的功能抽取出来)。
二、配置参数
根据上边的结构分析,我们很容易想到组件的配置参数:dateStart:日期的下限,可选,默认为1900/01/01;
dateEnd:日期的上限,可选,默认为当天;
dateDefault:初始化时默认选中的日期,可选,可以为input里的日期值;
id:input的id,js将会将select动态加到input节点的后面。
组件的使用方法如下:
var date = new DateCascade(id, { dateStart: xxxx/xx/xx, dateEnd: xxxx/xx/xx, dateDefault: xxxx/xx/xx });
三、对外提供的接口
师兄说过,开发一个组件,先想好其接口方法,再思考实现其功能的逻辑方法。这个组件提供了一些常用的方法:render:渲染结构,这个方法在初始化init的时候调用了一次,后面可以多次调用更改其参数重新渲染结构(不影响之前绑定过的事件);
//上下限均为默认值,初始化时已经调用过一次render方法 var dt = new DateCascade('birthday',{}); //重新设置上下限及默认选中值 dt.render({ dateStart: '1930/01/01', dateEnd: '2012/12/21', dateDefault: '1989/11/05' });
renderYear,renderMonth,renderDay:分别渲染年月日的select结构;
setNewDate:设置新的选中日期,参数形式有两种:
dt.setNewDate('2011/01/20');// 传进Date值 dt.setNewDate('2011', '01', '20'); //按年月日传进参数,主要考虑到传进的年月日可能有空值
setNewYear,setNewMonth,setNewDay:分别设置新的年月日;
四、逻辑的实现
实现逻辑的思路很简单:知道年月日的可选择范围(如1月的可选天数范围为1-31) -> 渲染相应的日期结构 -> 设置选中日期。所以起始点在于获取年月日的可选择范围:
/** * 获取年份区间 * @method _getYearRange * @return {Object} * @private */ _getYearRange: function(){ var that = this; return { min: that._dsYear, max: that._deYear }; }, /** * 获取月份区间 * @method _getMonthRange * @param y {Number} * @return {Object} * @private */ _getMonthRange: function(y){ var that = this, min = 1, max = 12; if (y == that._dsYear) { min = that._dsMonth; } if (y == that._deYear) { max = that._deMonth; } return { min: min, max: max }; }, /** * 获取天数区间 * @method _getMonthRange * @param y {Number} * @param m {Number} * @return {Object} * @private */ _getDayRange: function(y, m){ var that = this, min = 1, max = 31; if (m) { if (m != 2) { max = that._dayInMonth[m - 1]; } else { max = that._isLeapYear(y) ? 29 : 28; } if (y == that._dsYear && m == that._dsMonth) { min = that._dsDay; } if (y == that._deYear && m == that._deMonth) { max = that._deDay; } } return { min: min, max: max }; }
然后粗略浏览一下实现整个组件的逻辑方法:(细节请看这里)
var DateCascade = function(){ //构造函数 function DateCascade(){ this._init.apply(this, arguments); } //函数原型 DateCascade.prototype = { constructor: DateCascade, _init: function(id, config){ //初始化 }, _buildParam: function(config){ //设置参数 }, _bindEvent: function(){ //绑定change事件 }, render: function(config){ //渲染结构 }, _isLeapYear: function(y){ //是否是闰年 }, _isDate: function(d){ //检查是否是正确的Date格式 }, _checkDate: function(date){ //是否是正确的Date或可形成Date的字符串 }, setNewDate: function(/*y, m, d*/) { //设置新日期 }, setNewYear: function(y, l){ //设置新年份 }, setNewMonth: function(m, l){ //设置新月份 }, setNewDay: function(d){ //设置新天数 }, renderYear: function(){ //渲染年份 }, renderMonth: function(y){ //渲染月份 }, renderDay: function(y, m){ //渲染天数 }, _getYearRange: function(){ //获取年份区间 }, _getMonthRange: function(y){ //获取月份区间 }, _getDayRange: function(y, m){ //获取天数区间 } }; return DateCascade; }();
五、其他
其他的一些细节及应该注意的地方render年月日结构时使用原生的select.add(new Option(text, value), undefined)的形式添加选项,速度快(原来的组件使用YUI方法添加的,IE6下会出现渲染延迟的bug);
将日期值转成数值时要用parseInt(v, 10),别忘了后面的参数10,因为如果v是以0开头的数最后会转成八进制数,而日期经常会出现以0开头的数;
选中值的时候可以直接使用select.value = v的形式,而不用一个个option的匹配;
用3的方法选值,如果选项里没有对应的值,不同浏览器的处理不一样,除IE外的浏览器会默认选中第一个选项,而IE不会选中任何值,因此要hack一下;
添加了getSelectedDate(取得选中日期的值)和updateInput(更新input里的值)的方法;
没有设置回调函数的功能(个人觉得没必要)
六、下载
demo及源码下载(含YUI新版本)// /**
* DateCascade 日期级联组件
* @author 虎牙 huya.nzb@taobao.com/ningzbruc@gmail.com
* @date 2011-01-31
*/
var DateCascade = function(){
/**
* 日期级联组件模块
* @module datecascade
* @requires base, node
*/
/**
* 公共函数
*/
function $(id){
return (id) && (typeof id === 'string' ? document.getElementById(id) : id);
}
function addEvent(elem, event, foo, capture){
if (elem.addEventListener) {
elem.addEventListener(event, foo, !!capture);
} else if (elem.attachEvent) {
elem.attachEvent('on' + event, foo);
} else {
elem['on' + event] = foo;
}
}
function removeEvent(elem, event, foo, capture){
if (elem.removeEventListener) {
elem.removeEventListener(event, foo, !!capture);
} else if (elem.detachEvent) {
elem.detachEvent('on' + event, foo);
} else {
elem['on' + event] = null;
}
}
function insertAfter(node, rel){
var parent = rel.parentNode;
if (parent.lastChild == rel) {
parent.appendChild(node);
}
else {
parent.insertBefore(node, rel.nextSibling);
}
}
/**
* 日期级联组件
* @class DateCascade
* @constructor
*/
function DateCascade(){
this._init.apply(this, arguments);
}
DateCascade.prototype = {
/**
* 构造函数属性
* @attribute
*/
constructor: DateCascade,
/**
* 初始化
* @method _init
* @param {String} id 相关的input的id
* @param {Object} config 配置参数
* @private
*/
_init: function(id, config){
var that = this, doc = document;
if (!(that._input = $(id))) {
return;
}
that._input.style.display = 'none';
that._Y = doc.createElement('select');
that._Y.id = id + '_selectyear';
insertAfter(that._Y, that._input);
that._M = doc.createElement('select');
that._M.id = id + '_selectmonth';
insertAfter(that._M, that._Y);
that._D = doc.createElement('select');
that._D.id = id + '_selectday';
insertAfter(that._D, that._M);
that._dayInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
that.render(config);
that._bindEvent();
},
/**
* 构造参数
* @method _buildParam
* @param {Object} config 配置参数
* @private
*/
_buildParam: function(config){
config = config || {};
var that = this, _df = that._input.value;
that.dateStart = (config.dateStart && that._checkDate(config.dateStart)) || that.dateStart || new Date('1900/01/01');
that.dateEnd = (config.dateEnd && that._checkDate(config.dateEnd)) || that.dateEnd || new Date();
that.dateDefault = config.dateDefault && that._checkDate(config.dateDefault) || that.dateDefault || (_df && that._checkDate(_df));
},
/**
* 绑定事件
* @method _bindEvent
* @private
*/
_bindEvent: function(){
var that = this;
addEvent(that._Y, 'change', function(e){
that.setNewMonth(parseInt(that._M.value, 10));
});
addEvent(that._M, 'change', function(e){
that.setNewDay(parseInt(that._D.value, 10));
});
},
/**
* 渲染结构
* @method render
* @param {Object} 配置参数
* @chainable
*/
render: function(config){
var that = this;
that._buildParam(config);
var _ds = that.dateStart, _de = that.dateEnd, _df = that.dateDefault, _y = that._Y.value, _m = that._M.value, _d = that._D.value;
that._dsYear = _ds.getFullYear();
that._dsMonth = _ds.getMonth() + 1;
that._dsDay = _ds.getDate();
that._deYear = _de.getFullYear();
that._deMonth = _de.getMonth() + 1;
that._deDay = _de.getDate();
if (_df) {
that.setNewDate(_df);
} else if (_y || _m || _d) {
that.setNewYear(_y, false).setNewMonth(_m, false).setNewDay(_d);
} else {
that.renderYear().renderMonth().renderDay();
that._Y.options[0].selected = true;
that._M.options[0].selected = true;
that._D.options[0].selected = true;
}
return this;
},
/**
* 检查日期是否在给定的区间内
* @method _inDateRange
* @param {Date} date
* @private
* @return {Boolean}
*/
_inDateRange: function(date){
var that = this, date = that._checkDate(date);
if (!date) {
return;
}
var start = that._dsYear * 10000 + that._dsMonth * 100 + that._dsDay, end = that._deYear * 10000 + that._deMonth * 100 + that._deDay, date = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate();
if (date < start || date > end) {
return false;
}
return true;
},
/**
* 是否是闰年
* @method _isLeapYear
* @param {String | Number} y
* @private
* @return {Boolean}
*/
_isLeapYear: function(y){
y = parseInt(y, 10);
return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0);
},
/**
* 是否是Date格式
* @method _isDate
* @param {Date} d
* @private
* @return {Boolean}
*/
_isDate: function(d){
return Object.prototype.toString.call(d) === '[object Date]' && d.toString() !== 'Invalid Date' && !isNaN(d);
},
/**
* 检查是否是正确的Date格式
* @method _checkDate
* @param {Date} d
* @private
* @return {Date | Boolean} 如果正确则返回Date,否则返回false
*/
_checkDate: function(date){
return date && ((this._isDate(date) && date) || (this._isDate(new Date(date)) && new Date(date)));
},
/**
* 取得当前选中的日期
* @method getSelectedDate
* @return {String} 返回格式如 2011-02-28
*/
getSelectedDate: function(){
var that = this, y = that._Y.value, m = that._M.value, d = that._D.value;
if (!y || !m || !d) {
return '';
}
return y + '-' + m + '-' + d;
},
/**
* 更新隐藏的input的值
* @method updateInput
* @chainable
*/
updateInput: function(){
var v = this.getSelectedDate();
v && (this._input.value = v);
return this;
},
/**
* 设置新日期
* @method setNewDate
* @param {Date} date optional
* @param {String | Number} y optional
* @param {String | Number} m optional
* @param {String | Number} d optional
* @chainable
*/
setNewDate: function(/*y, m, d*/) {
var that = this, args = arguments;
if (args.length === 3) {
var y = args[0], m = args[1], d = args[2];
} else if (args.length === 1) {
var date = that._checkDate(args[0]);
if (!date) { return this; }
var y = date.getFullYear(), m = date.getMonth() + 1, d = date.getDate();
} else {
return this;
}
that.setNewYear(y, false).setNewMonth(m, false).setNewDay(d);
return this;
},
/**
* 设置新年份
* @method setNewYear
* @chainable
* @param y {String}
* @param l {Boolean} linkage 改变年份时是否联动月份及天数 默认为true
*/
setNewYear: function(y, l){
var that = this, m = parseInt(that._M.value, 10), d = parseInt(that._D.value, 10);
that.renderYear();
that._Y.value = y;
//IE Bugfix, 上一行代码在下拉框没有相对应的值的时候,除IE外的浏览器默认选中第一个选项
//IE不选中任何选项,默认为空
if (that._Y.selectedIndex < 0) {
that._Y.options[0].selected = true;
}
if (l === false) {
//更新input的值
that.updateInput();
return this;
}
that.setNewMonth(m);
return this;
},
/**
* 设置新月份
* @method setNewMonth
* @chainable
* @param m {String}
* @param l {Boolean} linkage 改变年份时是否联动天数 默认为true
*/
setNewMonth: function(m, l){
var that = this, y = parseInt(that._Y.value, 10), d = parseInt(that._D.value, 10), m = (parseInt(m, 10) < 10 ? '0' : '') + parseInt(m, 10);
that.renderMonth(y);
that._M.value = m;
if (that._M.selectedIndex < 0) {
that._M.options[0].selected = true;
}
if (l === false) {
that.updateInput();
return this;
}
that.setNewDay(d);
return this;
},
/**
* 设置新天数
* @method setNewDay
* @chainable
* @param d {String}
*/
setNewDay: function(d){
var that = this, y = parseInt(that._Y.value, 10), m = parseInt(that._M.value, 10), d = (parseInt(d, 10) < 10 ? '0' : '') + parseInt(d, 10);
that.renderDay(y, m);
that._D.value = d;
if (that._D.selectedIndex < 0) {
that._D.options[0].selected = true;
}
that.updateInput();
return this;
},
/**
* 渲染年份结构
* @method renderYear
* @chainable
*/
renderYear: function(){
var that = this, r = that._getYearRange(), options = that._Y.options, l = options.length, i, newOption;
//年份是从大到小排列,如果不变,则不进行渲染
if (l > 1 && options[1].value == r.max && options[l - 1].value == r.min) {
return this;
}
that._Y.innerHTML = '';
that._Y.add(new Option('-', ''), undefined);
for (i = r.max; i >= r.min; i--) {
newOption = new Option(i, i);
that._Y.add(newOption, undefined);
}
return this;
},
/**
* 渲染月份结构
* @method renderMonth
* @param {String | Number} 当前年份
* @chainable
*/
renderMonth: function(y){
var that = this, r = that._getMonthRange(y), options = that._M.options, l = options.length, i, newOption, t;
if (l > 1 && options[1].value == r.min && options[l - 1].value == r.max) {
return this;
}
that._M.innerHTML = '';
that._M.add(new Option('-', ''), undefined);
for (i = r.min; i <= r.max; i++) {
t = i >= 10 ? i : '0' + i;
newOption = new Option(t, t);
that._M.add(newOption, undefined);
}
return this;
},
/**
* 渲染天数结构
* @method renderDay
* @param {String | Number} y 当前年份
* @param {String | Number} m 当前月份,不以0开头的十进制数
* @chainable
*/
renderDay: function(y, m){
var that = this, r = that._getDayRange(y, m), options = that._D.options, l = options.length, i, newOption, t;
if (l > 1 && options[1].value == r.min && options[l - 1].value == r.max) {
return this;
}
that._D.innerHTML = '';
that._D.add(new Option('-', ''), undefined);
for (i = r.min; i <= r.max; i++) {
t = i >= 10 ? i : '0' + i;
newOption = new Option(t, t);
that._D.add(newOption, undefined);
}
return this;
},
/**
* 获取年份区间
* @method _getYearRange
* @return {Object}
* @private
*/
_getYearRange: function(){
var that = this;
return {
min: that._dsYear,
max: that._deYear
};
},
/**
* 获取月份区间
* @method _getMonthRange
* @param y {Number}
* @return {Object}
* @private
*/
_getMonthRange: function(y){
var that = this, min = 1, max = 12;
if (y == that._dsYear) {
min = that._dsMonth;
}
if (y == that._deYear) {
max = that._deMonth;
}
return {
min: min,
max: max
};
},
/**
* 获取天数区间
* @method _getMonthRange
* @param y {Number}
* @param m {Number}
* @return {Object}
* @private
*/
_getDayRange: function(y, m){
var that = this, min = 1, max = 31;
if (m) {
if (m != 2) {
max = that._dayInMonth[m - 1];
}
else {
max = that._isLeapYear(y) ? 29 : 28;
}
if (y == that._dsYear && m == that._dsMonth) {
min = that._dsDay;
}
if (y == that._deYear && m == that._deMonth) {
max = that._deDay;
}
}
return {
min: min,
max: max
};
}
};
return DateCascade;
}();
// ]]>
// window.onload = function() {
var date = new DateCascade('birthday');
};
// ]]>
相关文章推荐
- 我的组件库--java Date Picker 日历 日期选择器
- react-native DatePicker日期选择组件的实现
- Android表单组件,单选,多选,下拉列表,日期选择,时间选择
- Delphi for iOS开发指南(5):在iOS应用程序中使用Calendar组件来选择日期
- react-native DatePicker日期选择组件的实现代码
- react-native DatePicker日期选择组件的实现
- JavaScript:日期选择器组件的使用
- Android开发之日期时间选择组件事…
- Delphi for iOS开发指南(5):在iOS应用程序中使用Calendar组件来选择日期
- 设置日期选择组件(DateField )的选择范围
- 介绍一款Android日期选择组件Mobi Pick
- 设置日期选择组件(DateField )的选择范围
- 一个用在java项目中的日期选择组件
- 设置日期选择组件DateField年选择范围和上下选择按钮.
- My97DatePicker -- 一个功能丰富, 而且兼容 ie 6, 7的日期选择组件
- 弹出式日期选择组件日历显示VS2005重写部分
- Andriod组件之日期选择器DatePicker
- 微信小程序-picker组件地区,时间,日期选择
- [js开源组件开发]js手机联动选择日期 开源git