Customizing jQuery EasyUI DateBox for Granular Year-Month Selection and Period-Driven Formatting
The following implementation demonstrates how to restrict the jQuery EasyUI datebox component to year-month granularity while dynamically adjusting its display behavior based on an external period selector. Additionally, a lightweight alternative using combobox is provided for single-year selection scenarios.
HTML Structure
<input id="yymmSelector" class="easyui-datebox" data-options="editable:false">
<select id="periodSelector" class="easyui-combobox" data-options="value:'YM',prompt:'Select Period'">
<option value="YM">Year-Month</option>
<option value="Y">Year Only</option>
</select>
Configuration & Event Handling
Initialize the date picker by intercepting the panel expansion event to hijack the day-view rendering process. This forces the UI to display only the month grid while suppressing the calendar tooolbar and day cells.
function initializeDateControl() {
const $picker = $('#yymmSelector');
let monthGridElements = null;
let triggerSpan = null;
$picker.datebox({
onShowPanel: function () {
if (!triggerSpan) {
triggerSpan = $picker.parent().find('span.calendar-text');
}
// Force month view by simulating header click
triggerSpan.trigger('click');
if (!monthGridElements) {
setTimeout(() => {
const panel = $picker.datebox('panel');
monthGridElements = panel.find('div.calendar-menu-month-inner td');
monthGridElements.on('click.yymod', function (e) {
e.stopPropagation();
const currentYear = /\d{4}/.exec(triggerSpan.text())[0];
const selectedMonth = parseInt($(this).attr('abbr'), 10) + 1;
$picker.datebox('hidePanel');
$picker.datebox('setValue', `${currentYear}-${String(selectedMonth).padStart(2, '0')}`);
});
}, 50);
}
},
parser: function (valStr) {
if (!valStr) return new Date();
const [yearPart, monthPart] = valStr.split('-');
return new Date(parseInt(yearPart, 10), parseInt(monthPart, 10) - 1, 1);
},
formatter: function (dateObj) {
const periodMode = $('#periodSelector').combobox('getValue');
if (periodMode === 'Y') {
return String(dateObj.getFullYear());
}
const mm = String(dateObj.getMonth() + 1).padStart(2, '0');
return `${dateObj.getFullYear()}-${mm}`;
}
});
// Set default value to current month
const now = new Date();
const defaultVal = formatDateForPicker(now);
$picker.datebox('setValue', defaultVal);
}
function formatDateForPicker(targetDate) {
const mode = $('#periodSelector').combobox('getValue');
if (mode === 'Y') return String(targetDate.getFullYear());
const m = targetDate.getMonth() + 1;
return `${targetDate.getFullYear()}-${String(m).padStart(2, '0')}`;
}
Dynamic Value Synchronization
Link the exetrnal period dropdown to the date control so that switching modes automatically recalcualtes and formats the displayed value according to the selected granularity.
function syncValueBasedOnPeriod() {
const mode = $('#periodSelector').combobox('getValue');
const existingVal = $('#yymmSelector').datebox('getValue');
const today = new Date();
let calculatedValue = '';
if (mode === 'Y') {
calculatedValue = String(today.getFullYear());
if (existingVal.includes('-')) {
$('#yymmSelector').datebox('setValue', calculatedValue);
}
} else {
const nextMonth = today.getMonth() === 11 ? 0 : today.getMonth();
const adjYear = today.getMonth() === 11 ? today.getFullYear() - 1 : today.getFullYear();
calculatedValue = `${adjYear}-${String(nextMonth + 1).padStart(2, '0')}`;
if (!existingVal.includes('-')) {
$('#yymmSelector').datebox('setValue', calculatedValue);
}
}
}
// Attach listener
$('#periodSelector').on('change.combobox', syncValueBasedOnPeriod);
Alternative: Standalone Year-Only Selector
When only year selection is required, replacing the datebox with a combobox reduces DOM overhead and simplifies state management.
function setupYearDropdown() {
const startYear = 2018;
const endYear = 2030;
const options = Array.from({ length: endYear - startYear + 1 }, (_, i) => {
const y = startYear + i;
return { timeName: String(y), timeCode: String(y) };
});
$('#yymmSelector').combobox({
data: options,
editable: false,
valueField: 'timeCode',
textField: 'timeName',
onSelect: function (rec) {
console.log('Selected Year:', rec.timeName);
}
});
const currentYear = new Date().getMonth() > 0 ? new Date().getFullYear() : new Date().getFullYear() - 1;
$('#yymmSelector').combobox('setValue', String(currentYear));
}