本文介绍如何在 FullCalendar 6+ 中突破默认 title 限制,通过手动 DOM 更新与日历实例联动,在页眉区域动态显示「上上月、上月、本月、下月、下下月」共五个月份名称,并支持 prev/next 按钮同步刷新。
本文介绍如何在 fullcalendar 6+ 中突破默认 `title` 限制,通过手动 dom 更新与日历实例联动,在页眉区域动态显示「上上月、上月、本月、下月、下下月」共五个月份名称,并支持 prev/next 按钮同步刷新。
FullCalendar 默认的 headerToolbar 中 title 仅能渲染当前视图月份(如 "August 2024"),无法直接展示相邻月份。但实际业务中常需类似 "July • August • September" 的横向月导航结构。本方案不依赖插件或魔改源码,而是采用「分离渲染 + 事件监听」策略:禁用内置 title,改用自定义 HTML 元素承载月份文本,并通过 calendar.prev() / calendar.next() 后主动更新 DOM。
✅ 实现步骤概览
- 移除默认标题:将 headerToolbar 的 left/center/right 全部设为空对象 {},避免干扰;
-
构建自定义页眉 HTML:在日历容器外(或内)添加带 ID 的 元素,例如:
<div class="custom-header"> <button id="prevButton" class="fc-button">◀</button> <span id="previous-month-2"></span> <span id="previous-month-1"></span> <span id="current-month" class="current-month"></span> <span id="next-month-1"></span> <span id="next-month-2"></span> <button id="nextButton" class="fc-button">▶</button> </div> <div id="calendar"></div>
- 初始化时计算并填充月份:使用 calendar.getDate() 获取当前日期,配合 moment.js(或原生 Intl.DateTimeFormat)格式化各月份名称;
- 绑定按钮事件并同步更新:每次调用 calendar.prev() 或 calendar.next() 后,重新获取 calendar.getDate(),再批量更新所有 文本。
? 关键代码实现(兼容 FullCalendar v6+)
⚠️ 注意:以下示例基于 moment.js(推荐 v2.30+)。若需零依赖,可替换为 原生日期格式化方案(见文末说明)。
function drawCalendar() {
const calendarEl = document.getElementById('calendar');
const calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
fixedWeekCount: false,
firstDay: 1,
displayEventTime: false,
height: 700,
dayMaxEventRows: true,
views: {
timeGrid: { dayMaxEventRows: 6 }
},
// ❌ 禁用内置标题栏
headerToolbar: { left: '', center: '', right: '' },
events: eventsArray,
eventClick: showEvent,
eventDidMount: ({ event, el }) => {
if (event.backgroundColor) el.style.background = event.backgroundColor;
}
});
// ? 获取 DOM 元素引用
const prev2El = document.getElementById('previous-month-2');
const prev1El = document.getElementById('previous-month-1');
const currEl = document.getElementById('current-month');
const next1El = document.getElementById('next-month-1');
const next2El = document.getElementById('next-month-2');
// ? 初始化月份文本(使用 moment)
function updateMonthLabels() {
const date = calendar.getDate();
const m = moment(date);
prev2El.textContent = m.clone().subtract(2, 'months').format('MMMM');
prev1El.textContent = m.clone().subtract(1, 'months').format('MMMM');
currEl.textContent = m.format('MMMM');
next1El.textContent = m.add(1, 'months').format('MMMM');
next2El.textContent = m.add(1, 'months').format('MMMM'); // 即 +3 个月
}
// ? 绑定导航按钮
document.getElementById('prevButton').addEventListener('click', () => {
calendar.prev();
updateMonthLabels();
});
document.getElementById('nextButton').addEventListener('click', () => {
calendar.next();
updateMonthLabels();
});
// ? 首次渲染前初始化
updateMonthLabels();
calendar.render();
}
⚠️ 重要注意事项
- 时区一致性:calendar.getDate() 返回的是日历内部时区(UTC 或浏览器本地),确保 moment(date) 解析逻辑与之对齐;建议统一使用 moment(date).locale('ru') 适配多语言;
- 性能优化:updateMonthLabels() 是轻量级操作,无需防抖;但若页面含大量日历实例,请封装为可复用函数;
-
响应式适配:为防止长月份名(如俄语 августа)溢出,建议为 .custom-header span 添加 CSS:
.custom-header span { min-width: 80px; text-align: center; font-weight: 500; } .current-month { font-weight: 700; color: #333; } - 无障碍访问(a11y):为按钮添加 aria-label,例如 aria-label="Перейти к предыдущему месяцу",提升可访问性。
? 原生替代方案(无需 moment.js)
若项目禁止引入 moment,可用 Intl.DateTimeFormat 替代:
function getMonthName(date, locale = 'ru', options = { month: 'long' }) {
return new Intl.DateTimeFormat(locale, options).format(date);
}
// 使用示例:
const now = calendar.getDate();
const curr = getMonthName(now); // "август"
const prev1 = getMonthName(new Date(now.getFullYear(), now.getMonth() - 1, 1)); // "июль"
✅ 优势:零依赖、体积小、现代浏览器全覆盖(IE11 需 polyfill)。
✅ 总结
FullCalendar 的灵活性在于它暴露了完整的日历状态(getDate())、导航方法(prev()/next())和生命周期钩子。本文方案正是利用这一特性,以「可控 DOM + 主动同步」的方式,优雅地实现了超越默认能力的标题定制需求。无论是俄语、中文还是其他语言环境,只需调整 locale 和格式化选项即可无缝适配。掌握此模式后,你还能轻松扩展为季度切换、年份范围滚动等高级场景。










