我试图自定义jQueryUI的datepicker为我的客户做一些很酷的事情,但我被困了。我已在日历中添加了自定义列,以显示每周的租借费率。遗憾的是,jQueryUI的日历通过在每次点击时重绘自身来实现,因此我的自定义列被删除了,显然如果它们改变月份也会发生这种情况。
我正在寻找最佳做法来检测我的专栏是否已经消失并重新绘制。我希望jQueryUI.datepicker解雇某种"完成"事件完成后,但事情并非如此。所有它的钩子都是在绘制日历之前。任何帮助表示赞赏。
代码
/**
* A Wrapper for jQueryUI.datepicker.
*/
(function( factory ){
factory( jQuery );
}( function( $ ){
function MpCalendar(options) {
options = options || {}
this._curInst = null;
this._defaults = {
highlightDateCSS: "mpcalendar-highlight",
weeklyRateCSS: "mpcalendar-weekly-rate",
weeklyRateHeading: "Rate"
};
this.settings = $.extend({}, this._defaults, options);
};
$.extend(MpCalendar.prototype , {
/* Create a new instance of the object. */
_newInst: function(target) {
return {
element: target,
blockDates: {},
weeklyRates: [],
input1: [],
input2: []
};
},
/* Retrieve a previous instance of the object. */
_getInst: function(target) {
try {
return $.data(target, "mpcalendar");
} catch(e) {
throw "Missing instance for this MpCalendar.";
}
},
/* Attach the calendar to the target element */
_attachMpCalendar: function(target, settings) {
//Check that we were given a div or span. We're only making inline calendars.
var nodeName = target.nodeName.toLowerCase();
if(nodeName !== ("div" || "span"))
throw new Error('Target must be a div or span got "'+nodeName+'" instead.');
var self = this;
var inst = this._newInst($(target));
inst.settings = $.extend({}, settings || {}, {
beforeShowDay: function(date) {
return self._beforeShowDay(inst, date);
},
onSelect: function(date, datepicker) {
return self._onSelect(inst, date, datepicker);
}
});
//Make sure showWeek is true.
inst.settings.showWeek = true;
//Make sure we have inputs to use.
inst.input1 = $(inst.element.data('mpinput1'));
if(!inst.input1.length)
throw new Error('Could not find mpinput1.');
inst.input2 = $(inst.element.data('mpinput2'));
if(!inst.input2.length)
throw new Error('Could not find mpinput2.');
//Initiate block dates found in the settings.
if(typeof inst.settings.blockDates === "object")
this._setBlockDates(inst, inst.settings.blockDates);
//Initiat weekly rates found in the settings.
if(typeof inst.settings.weeklyRates === "object")
this._setWeeklyRates(inst, inst.settings.weeklyRates);
//Initiate datepicker.
inst.element.datepicker(inst.settings);
//Draw extra rates column.
this._attachRates(inst);
//Store our instance.
$.data(target, "mpcalendar", inst);
},
/* Set block dates with the given list of dates */
_setBlockDatesMpCalendar: function(target, dates) {
if(typeof dates !== "object")
throw new Error('Expected dates to be an "object" got "' + typeof dates + '" instead.');
var inst = this._getInst(target);
this._setBlockDates(inst, dates);
},
/* Add a given date to the block list */
_addBlockDateMpCalendar: function(target, date, status) {
var inst = this._getInst(target);
this._addBlockDate(inst, date, status);
},
/* Remove a given date from the block list */
_removeBlockDateMpCalendar: function(target, date) {
var inst = this._getInst(target);
this._removeBlockDate(inst, date);
},
/* Set Weekly Rates with the given list of rates */
_setWeeklyRatesMpCalendar: function(target, rates) {
if(!(Array.isArray(rates)))
throw new Error('Expected rates to be an "array" got "' + typeof rates + '" instead.');
var inst = this._getInst(target);
this._setWeeklyRates(inst, rates);
},
/* Set the Rate for a single Week */
_setWeeklyRateMpCalendar: function(target, week, rate) {
if(typeof week !== "number")
week = parseInt(week);
if(typeof rate !== "number")
rate = parseFloat(rate);
var inst = this._getInst(target);
this._setWeeklyRate(inst, week, rate);
},
/**
* Return an array of Date objects contianing the dates selected on the calendar.
*
* @param {object} target
* @returns {Array}
*/
_getSelectedDatesMpCalendar: function(target) {
var inst = this._getInst(target);
return this._getSelectedDates(inst);
},
/**
* Return the CSS Class used for the specified date or false if the date is not blocked.
*
* @param {object} target
* @param {Date} date
* @returns {string}
*/
_isBlockedDateMpCalendar: function(target, date) {
var inst = this._getInst(target);
return this._isBlockedDate(inst, date);
},
/* Attach our custom weekly rates column */
_attachRates: function(inst) {
var self = this;
//Attach header and empty rates.
var heading = $('<th>'+ this.settings.weeklyRateHeading +'</th>');
var tdata = $('<td class="'+this.settings.weeklyRateCSS+'"></td>');
inst.element.find('.ui-datepicker-calendar thead tr').append(heading);
inst.element.find('.ui-datepicker-calendar tbody tr').append(tdata);
inst.element.find('td.ui-datepicker-week-col').each(function(){
var week = parseInt($(this).text());
var rate = inst.weeklyRates[week] || "Test";
$(this).closest('tr').find('.'+ self.settings.weeklyRateCSS).html(rate);
});
},
_isBlockedDate: function(inst, date) {
if(!(date instanceof Date))
throw new Error('Expected date to be instance of Date.');
try {
var vacancyStatus = inst.blockDates[date.getFullYear()][date.getMonth()][date.getDate()] || false;
return vacancyStatus;
} catch(e) {
}
return false;
},
_getSelectedDates: function(inst) {
var dates = [];
try {
var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, inst.input1.val());
var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, inst.input2.val());
if((date1 || date2) === null)
return dates;
while(date1 <= date2) {
dates.push(new Date(date1));
date1.setDate(date1.getDate() + 1);
}
} catch(e) {
//Guess we don't have any dates.
}
return dates;
},
_setBlockDates: function(inst, dates) {
inst.blockDates = {};
for(var date in dates) {
if(typeof dates[date] !== 'string')
continue;
this._addBlockDate(inst, date, dates[date]);
}
},
_setWeeklyRates: function(inst, rates) {
inst.weeklyRates = [];
for(var week in rates) {
var rate = rates[week];
if(typeof week !== 'number')
week = parseInt(week);
if(typeof rate !== 'number')
rate = parseFloat(rate);
this._setWeeklyRate(inst, week, rate);
}
},
_removeBlockDate: function(inst, date) {
try {
var datetime = new Date(date);
var day = datetime.getDate();
var month = datetime.getMonth();
var year = datetime.getFullYear();
delete inst.blockDates[year][month][day];
} catch(e) {
//The date probably never existed any way.
}
},
_addBlockDate: function(inst, date, status) {
if(typeof status !== "string")
throw new Error('Expected class name to be typeof "string" got "' + typeof status + '".');
try {
var datetime = new Date(date);
var day = datetime.getDate();
var month = datetime.getMonth();
var year = datetime.getFullYear();
if(typeof inst.blockDates[year] !== "object")
inst.blockDates[year] = {};
if(typeof inst.blockDates[year][month] !== "object")
inst.blockDates[year][month] = {};
inst.blockDates[year][month][day] = status;
} catch(e) {
throw new Error('Error adding block date: "' + e.message + '".');
}
},
_setWeeklyRate: function(inst, week, rate) {
inst.weeklyRates[week] = rate;
},
/* Function attached to datepicker's beforeShowDay, handles showing blocked dates and range selection */
_beforeShowDay: function(inst, date) {
var cssClasses = [];
try {
var vacancyStatus = inst.blockDates[date.getFullYear()][date.getMonth()][date.getDate()];
if(vacancyStatus !== undefined)
cssClasses.push(vacancyStatus);
} catch(e) {
//There is no blockDate set.
}
try {
var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, inst.input1.val());
var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, inst.input2.val());
var highlight = ((date.getTime() === date1.getTime()) || (date2 && date >= date1 && date <= date2)) ? this.settings.highlightDateCSS : '';
cssClasses.push(highlight);
} catch(e) {
//Oh well.
}
if(cssClasses.length > 0)
return [true, cssClasses.join(' '), null];
return [true, '', null];
},
/* Function attached to datepicker's onSelect, allows for rangeselection */
_onSelect: function(inst, dateText, datepicker) {
var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, inst.input1.val());
var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, inst.input2.val());
var selectedDate = $.datepicker.parseDate($.datepicker._defaults.dateFormat, dateText);
if (!date1 || date2) {
inst.input1.val(dateText);
inst.input2.val("");
inst.element.datepicker('refresh');
} else if( selectedDate < date1 ) {
inst.input2.val( inst.input1.val() );
inst.input1.val( dateText );
inst.element.datepicker('refresh');
} else {
inst.input2.val(dateText);
inst.element.datepicker('refresh');
}
},
/* Because we are wrapping datepicker, this handles jQuery calls to internal functions for both MpCalendar and datepicker */
_callFunction: function(target, option) {
var otherArgs = Array.prototype.slice.call(arguments, 2);
if(typeof this["_"+option+"MpCalendar"] === "function")
return this["_"+option+"MpCalendar"].apply(this, [target].concat(otherArgs));
var inst = this._getInst(target);
inst.element.datepicker.apply(inst.element.datepicker(), [option].concat(otherArgs));
}
});
//jQuery extension for using MpCalendar.
$.fn.mpcalendar = function(options) {
var otherArgs = Array.prototype.slice.call(arguments, 1);
//If they are calling for one of our static methods, pass the call to MpCalendar and return the value.
if(typeof options === "string" && (options === "isBlockedDate" || options === "getSelectedDates"))
return $.mpcalendar["_"+options+"MpCalendar"].apply($.mpcalendar, [ this[0] ].concat(otherArgs));
//Else, call the appropriate function and return.
return this.each( function() {
typeof options === "string" ?
$.mpcalendar._callFunction.apply($.mpcalendar, [ this, options ].concat(otherArgs)) :
$.mpcalendar._attachMpCalendar(this, options);
});
};
$.mpcalendar = new MpCalendar();
return $.mpcalendar;
}));
我在小提琴原型中的小提琴:fiddle
我发现了一些关于自定义列的其他堆栈问题,但到目前为止还没有一个问题解决了如何处理更新的问题。我真的不想使用可能导致一些奇怪行为的setIntval()。并且我不确定在删除时附加事件是否有效,datepicker会在附加新绘制的日历之前在包含div上调用.empty()
,但这并不意味着我的删除事件会在日历甚至存在之前开始绘图?或者可能根本不绘制.empty()
为了避免内存泄漏,jQuery会在删除元素本身之前从子元素中删除其他构造(如数据和事件处理程序)。
提前致谢!
答案 0 :(得分:0)
所以我认为这是有效的。但是当日历出现在行之前时,屏幕上的元素会“闪烁”。我或许可以通过一些CSS解决这个问题,但如果有人有更好的想法,请告诉我。与此同时,我更新了原始问题中链接的小提琴。
代码
/* Attach our custom weekly rates column */
_attachRates: function(inst) {
var self = this;
//Attach header and empty rates.
var heading = $('<th>'+ this.settings.weeklyRateHeading +'</th>');
var tdata = $('<td class="'+this.settings.weeklyRateCSS+'"></td>');
inst.element.find('.ui-datepicker-calendar thead tr').append(heading);
inst.element.find('.ui-datepicker-calendar tbody tr').append(tdata);
inst.element.find('td.ui-datepicker-week-col').each(function(){
var week = parseInt($(this).text());
var rate = inst.weeklyRates[week] || "Test";
$(this).closest('tr').find('.'+ self.settings.weeklyRateCSS).html(rate);
});
inst.element.find('.ui-datepicker-calendar').first().on('remove', function(){
$(this).off("remove");
setTimeout(function(){
self._attachRates(inst);
}, 1);
});
},