You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为FullCalendar单元格添加hover效果及改造selectable触发方式?

嘿,我刚好在FullCalendar里折腾过类似的交互优化,给你分享两个亲测可行的方案:

一、给Agenda Week视图单元格添加Hover效果(显示+按钮)

你说的两个重叠表格是对的——FullCalendar的Agenda视图会生成.fc-bg(背景单元格层)和.fc-content-skeleton(内容展示层),直接给单个单元格绑hover事件容易因为动态生成或层级问题失效,用事件委托+操作背景层单元格是最稳妥的做法:

实现步骤:

  1. 给整个Agenda视图容器绑定mouseover/mouseout事件,通过事件冒泡定位到目标单元格
  2. 在背景单元格(.fc-bg td)里动态创建/显示+按钮,因为背景层是完整的单元格区域,不会被内容层的事件干扰

代码示例:

// 给日历的AgendaWeek视图容器绑定hover事件
const agendaWeekView = document.querySelector('.fc-agendaWeek-view');
if (agendaWeekView) {
  // 鼠标移入显示按钮
  agendaWeekView.addEventListener('mouseover', (e) => {
    const bgCell = e.target.closest('.fc-bg td');
    if (!bgCell) return;

    // 检查是否已创建按钮,避免重复生成
    let addBtn = bgCell.querySelector('.add-event-btn');
    if (!addBtn) {
      addBtn = document.createElement('button');
      addBtn.className = 'add-event-btn';
      addBtn.textContent = '+';
      // 按钮样式:绝对定位在单元格右上角
      Object.assign(addBtn.style, {
        position: 'absolute',
        top: '2px',
        right: '2px',
        width: '20px',
        height: '20px',
        borderRadius: '50%',
        border: 'none',
        backgroundColor: '#007bff',
        color: '#fff',
        cursor: 'pointer',
        fontSize: '14px',
        display: 'block'
      });
      bgCell.style.position = 'relative';
      bgCell.appendChild(addBtn);

      // 给按钮绑定点击事件,打开你的预约模态框
      addBtn.addEventListener('click', () => {
        const startTime = bgCell.dataset.date;
        // 这里调用你的模态框打开逻辑,传入startTime
        openBookingModal(startTime);
      });
    } else {
      addBtn.style.display = 'block';
    }
  });

  // 鼠标移出隐藏按钮
  agendaWeekView.addEventListener('mouseout', (e) => {
    const bgCell = e.target.closest('.fc-bg td');
    if (bgCell) {
      const addBtn = bgCell.querySelector('.add-event-btn');
      if (addBtn) addBtn.style.display = 'none';
    }
  });
}

额外CSS优化(可选):

.add-event-btn:hover {
  background-color: #0056b3;
}
/* 避免内容层元素遮挡按钮 */
.fc-content-skeleton .fc-event {
  pointer-events: auto; /* 保持事件点击,其他非交互内容元素可设为none */
}

二、将AgendaWeek的Selectable触发从点击改为Hover(兼顾性能)

原生FullCalendar的selectable确实只支持点击/拖拽触发,但我们可以禁用原生选中逻辑,通过节流的鼠标移动事件+原生API手动实现hover高亮,完全不用担心性能问题:

核心思路:

  1. 禁用原生selectable配置,自己实现选中逻辑
  2. 节流函数限制鼠标移动事件的触发频率(比如100ms一次),避免频繁DOM操作
  3. 通过单元格的data-*属性获取时间范围,调用FullCalendar的select()/unselect()方法实现高亮

代码示例:

// 初始化日历,禁用原生selectable
const calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
  plugins: ['interaction', 'timeGrid'],
  initialView: 'timeGridWeek',
  selectable: false, // 关闭原生选中
  slotDuration: '00:30:00', // 你的时间段间隔,比如30分钟
  // 其他配置...
});

// 节流函数:控制事件触发频率,避免性能浪费
function throttle(func, delay = 100) {
  let lastInvokeTime = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastInvokeTime >= delay) {
      func.apply(this, args);
      lastInvokeTime = now;
    }
  };
}

// 监听鼠标移动,节流处理
document.getElementById('calendar').addEventListener('mousemove', throttle((e) => {
  const bgCell = e.target.closest('.fc-bg td');
  if (!bgCell) {
    // 鼠标移出单元格,取消选中
    calendar.unselect();
    return;
  }

  // 获取单元格的开始时间
  const startDateStr = bgCell.dataset.date;
  let endDateStr;

  // 区分全天单元格和时间段单元格
  if (bgCell.dataset.time) {
    // 时间段单元格:计算结束时间(基于slotDuration)
    const startTime = new Date(`${startDateStr}T${bgCell.dataset.time}`);
    const endTime = new Date(startTime.getTime() + calendar.view.option('slotDuration'));
    endDateStr = endTime.toISOString().slice(0, 16); // 格式化为YYYY-MM-DDTHH:mm
  } else {
    // 全天单元格:结束时间为次日
    const endTime = new Date(startDateStr);
    endTime.setDate(endTime.getDate() + 1);
    endDateStr = endTime.toISOString().slice(0, 10); // 格式化为YYYY-MM-DD
  }

  // 调用FullCalendar原生方法高亮单元格
  calendar.select(startDateStr, endDateStr);
}, 100));

注意事项:

  • 节流间隔可以根据需求调整:50ms更灵敏,150ms更省性能,100ms是平衡流畅度和性能的最优值
  • 如果内容层的事件遮挡了鼠标检测,可以给非交互元素添加pointer-events: none,让鼠标事件穿透到背景层
  • 复用FullCalendar的select()方法会自动应用原生选中样式,不用自己写高亮CSS,兼容性更好

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

火山引擎 最新活动