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

FullCalendar 3.8在EventAfterAllRender中设置minTime/maxTime导致循环渲染

解决FullCalendar中eventAfterAllRender设置minTime/maxTime导致的无限循环问题

兄弟,我之前也踩过FullCalendar这个无限渲染的坑!你的问题根源很明确:

当你在eventAfterAllRender回调里修改minTime或maxTime这类会改变日历视图时间范围的选项时,FullCalendar会自动触发重新渲染;而重新渲染完成后,又会再次调用eventAfterAllRender回调,这样就形成了无限循环,导致日历反复刷新。

下面给你几个可行的解决方案,按推荐程度排序:

方案1:添加状态标记,避免重复执行(兼容性最好)

给代码加一个全局标记变量,用来判断当前是否正在更新时间范围,避免回调重复触发AJAX和选项修改:

(function(c) { 
  // 新增状态标记,防止循环执行
  let isUpdatingTimeRange = false;
  
  var calendarOptions = { 
    schedulerLicenseKey: '', 
    header : { 
      left : 'prevYear,prev today next,nextYear', 
      center: 'title', 
      right : 'month,agendaWeek,agendaDay' 
    }, 
    events : { 
      url : '{{ url("calendar/events-json-calendar") }}', 
      data: function () { 
        return { 
          id : $('#calendarsselect').val(), 
          showDeleted: $('#showDeletedEvents').is(':checked'), 
        }; 
      } 
    }, 
    selectable : true, 
    selectHelper : true, 
    theme : false, 
    slotDuration : '00:15:00', 
    defaultView : 'agendaWeek', 
    timeFormat : 'H:mm', 
    slotLabelFormat : 'H:mm', 
    editable : false, 
    firstDay : 1, 
    weekMode : 'liquid', 
    weekNumbers : true, 
    weekNumberTitle : "T: ", 
    minTime : '06:00:00', 
    maxTime : '20:00:00', 
    locale : 'sk', 
    scrollTime : '00:00', 
    aspectRatio : 1.9, 
    slotEventOverlap : false, 
    droppable : true, 
    lazyFetching : false, 
    eventAfterAllRender : function (view, element) { 
      // 如果正在更新时间范围,直接跳过本次回调
      if (isUpdatingTimeRange) return;
      
      var date = moment(c.fullCalendar('getDate')).format('YYYY-MM-DD'); 
      // 标记为正在更新
      isUpdatingTimeRange = true;
      
      $.ajax({ 
        url : '{{ url('calendar/worktime/get/minmaxtime') }}', 
        dataType: "json", 
        data : { 
          calendar_id : $('#calendarsselect').val(), 
          date : date 
        }, 
        success : function (data) { 
          console.log(data); 
          if ( data[0] != '23:59:59') { 
            c.fullCalendar('option','minTime',data[0]); 
          } 
          if ( data[1] != '00:00:00') { 
            c.fullCalendar('option','maxTime',data[1]); 
          }
          // 更新完成,重置标记
          isUpdatingTimeRange = false;
        },
        // 出错时也要重置标记,避免后续无法触发更新
        error: function() {
          isUpdatingTimeRange = false;
        }
      }); 
    } 
  }; 
  c.fullCalendar(calendarOptions); 
})($('#calendar'));

原理

通过isUpdatingTimeRange标记,确保同一时间只有一次AJAX请求和选项修改操作。当修改minTime/maxTime触发重新渲染后,下次进入eventAfterAllRender时会因为标记为true直接跳过,从而打破循环。

方案2:切换到更早的回调函数(更简洁)

把逻辑从eventAfterAllRender移到viewRender回调中——这个回调只会在视图初始化或切换时触发一次,修改时间范围选项不会再次触发它,自然不会形成循环:

(function(c) { 
  var calendarOptions = { 
    // ... 其他选项保持不变
    // 替换eventAfterAllRender为viewRender
    viewRender : function (view, element) { 
      // 从view对象获取当前视图的起始日期,比getDate更准确
      var date = moment(view.start).format('YYYY-MM-DD'); 
      
      $.ajax({ 
        url : '{{ url('calendar/worktime/get/minmaxtime') }}', 
        dataType: "json", 
        data : { 
          calendar_id : $('#calendarsselect').val(), 
          date : date 
        }, 
        success : function (data) { 
          console.log(data); 
          if ( data[0] != '23:59:59') { 
            c.fullCalendar('option','minTime',data[0]); 
          } 
          if ( data[1] != '00:00:00') { 
            c.fullCalendar('option','maxTime',data[1]); 
          }
        }
      }); 
    } 
  }; 
  c.fullCalendar(calendarOptions); 
})($('#calendar'));

注意事项

如果你的FullCalendar版本较新(v5+),viewRender已被废弃,需要改用datesRender或viewDidMount回调,逻辑是一样的。

方案3:提前获取时间范围再初始化日历(最彻底)

先通过AJAX获取初始日期的minTime/maxTime,再用这些值初始化日历,从根源避免初始化时的循环问题;同时在视图切换时再更新时间范围:

(function(c) { 
  // 获取初始日期和日历ID
  var initialDate = moment().format('YYYY-MM-DD');
  var calendarId = $('#calendarsselect').val();
  
  // 先请求时间范围
  $.ajax({ 
    url : '{{ url('calendar/worktime/get/minmaxtime') }}', 
    dataType: "json", 
    data : { 
      calendar_id : calendarId, 
      date : initialDate 
    }, 
    success : function (data) { 
      // 确定初始的minTime和maxTime
      var minTime = data[0] != '23:59:59' ? data[0] : '06:00:00';
      var maxTime = data[1] != '00:00:00' ? data[1] : '20:00:00';
      
      // 初始化日历
      var calendarOptions = { 
        // ... 其他选项保持不变
        minTime : minTime, 
        maxTime : maxTime, 
        // 视图切换时更新时间范围
        viewRender : function (view, element) { 
          var date = moment(view.start).format('YYYY-MM-DD'); 
          $.ajax({ 
            url : '{{ url('calendar/worktime/get/minmaxtime') }}', 
            dataType: "json", 
            data : { 
              calendar_id : $('#calendarsselect').val(), 
              date : date 
            }, 
            success : function (data) { 
              if ( data[0] != '23:59:59') { 
                c.fullCalendar('option','minTime',data[0]); 
              } 
              if ( data[1] != '00:00:00') { 
                c.fullCalendar('option','maxTime',data[1]); 
              }
            }
          }); 
        }
      }; 
      c.fullCalendar(calendarOptions); 
    }
  });
})($('#calendar'));

优势

这种方式完全避免了初始化阶段的循环问题,同时也能处理视图切换时的时间范围更新,逻辑最清晰。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:01:12