You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FullCalendar.js时间线日历交叉悬停效果行高适配问题

解决FullCalendar时间线日历交叉悬停行高错位问题

你的问题核心在于静态叠加表格的行高无法同步FullCalendar动态变化的行高——当事件是多行描述、同时间段多事件挤在一起时,日历行高会自动调整,但你手动加的表格行高还是固定值,自然会出现悬停层重叠错位。

下面是直接可行的解决方案,放弃静态叠加表格,基于FullCalendar自身DOM结构实现动态悬停:

1. 核心思路

  • 利用FullCalendar的datesRender事件(日历渲染/更新时触发),实时同步行的位置和尺寸,生成动态悬停触发层
  • 通过事件委托或直接绑定行元素的悬停事件,实现行+列的交叉高亮
  • 完全依赖日历自身DOM的实时尺寸,避免静态元素的错位问题

2. 具体代码实现

JavaScript 部分

document.addEventListener('DOMContentLoaded', function() {
  const calendarEl = document.getElementById('calendar');
  const calendar = new FullCalendar.Calendar(calendarEl, {
    // 保留你原来的日历配置(视图、事件数据等)
    initialView: 'timeGridWeek',
    events: [
      // 你的事件数据
    ],
    // 每次日历渲染/更新时同步悬停层
    datesRender: function(info) {
      // 先清除旧的悬停层
      document.querySelectorAll('.hover-overlay').forEach(el => el.remove());
      
      const timelineContainer = info.el.querySelector('.fc-timeGridWeek-view .fc-time-grid');
      // 获取所有日历行(包括事件行和空白槽位)
      const timelineRows = timelineContainer.querySelectorAll('.fc-time-grid-event-harness, .fc-time-grid-slot');
      
      timelineRows.forEach(row => {
        // 创建悬停触发层(覆盖整行,仅用于捕获鼠标事件)
        const overlay = document.createElement('div');
        overlay.className = 'hover-overlay';
        // 同步行的位置和尺寸
        const rowRect = row.getBoundingClientRect();
        const containerRect = timelineContainer.getBoundingClientRect();
        
        overlay.style.position = 'absolute';
        overlay.style.top = `${rowRect.top - containerRect.top}px`;
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = `${rowRect.height}px`;
        overlay.style.pointerEvents = 'auto';
        overlay.style.opacity = '0'; // 透明不影响视觉
        
        timelineContainer.appendChild(overlay);
        
        // 绑定悬停事件
        overlay.addEventListener('mouseenter', () => {
          // 高亮当前行
          row.classList.add('row-hover-highlight');
          // 高亮鼠标对应的列
          highlightCurrentColumn(timelineContainer, event.clientX);
        });
        
        overlay.addEventListener('mouseleave', () => {
          row.classList.remove('row-hover-highlight');
          clearColumnHighlight(timelineContainer);
        });
      });
    }
  });

  calendar.render();
});

// 辅助函数:高亮鼠标所在列
function highlightCurrentColumn(container, clientX) {
  const containerRect = container.getBoundingClientRect();
  const x = clientX - containerRect.left;
  const columns = container.querySelectorAll('.fc-day');
  
  columns.forEach(col => {
    const colRect = col.getBoundingClientRect();
    const colLeft = colRect.left - containerRect.left;
    const colRight = colRect.right - containerRect.left;
    
    if (x >= colLeft && x <= colRight) {
      col.classList.add('column-hover-highlight');
    }
  });
}

// 辅助函数:清除列高亮
function clearColumnHighlight(container) {
  container.querySelectorAll('.column-hover-highlight').forEach(el => {
    el.classList.remove('column-hover-highlight');
  });
}

CSS 部分

/* 行高亮样式 */
.row-hover-highlight {
  background-color: rgba(102, 178, 255, 0.15);
}

/* 列高亮样式 */
.column-hover-highlight {
  background-color: rgba(102, 178, 255, 0.15);
}

/* 日历容器需要开启相对定位,让悬停层能基于它定位 */
.fc-time-grid {
  position: relative;
}

3. 为什么之前的方法无效?

  • 静态表格的行高是预先设定的,无法响应FullCalendar动态调整的行高(比如多行事件、多事件叠加导致的行高拉伸)
  • clientHeight只能获取初始渲染的行高,无法监听后续行高变化;鼠标定位容易受滚动、缩放影响,精度不足
  • pointer-events:none会让悬停层失去事件捕获能力,无法触发高亮逻辑

内容的提问来源于stack exchange,提问作者user9737577

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 07:43:23