javascriptjquerycssfullcalendarfullcalendar-5

How to hide events in fullCalendar?


Using the latest version of fullCalendar (5.3.2) I want to hide some events that correspond to resources I don't want to show now in a given view. The standard way to do this is using an eventClassNames function to check for it and add a "hidden" class. Something like this:

eventClassNames: function(arg) {
   my_class = "";
   if (arg.view.type != 'resourceTimeGridDay') {
      if (arg.event.extendedProps.real_rc != "1") {
         my_class = 'hidden';
      }
   }
   return my_class;
}

using a simple CSS:

.fc-event.hidden {
  display: none;
}

This works fine, but has a problem when there is an overlap between a hidden event and a showed one. For instance in this case:

events: [
  {
    title: 'Resource 1',
    real_rc: '1',
    start: '2020-12-22 16:00',
    end: '2020-12-22 17:00'
  },
  {
    title: 'Resource 2',
    real_rc: '2',
    start: '2020-12-22 15:00',
    end: '2020-12-22 17:00'
  }
]

Only event with real_rc == 1 should be displayed, and in fact it is right, but the space used by the hidden event is reserved as you can see in this image:

Wrong result

If the event with the real_rc: 2 is ommited in the event list the result is the expected:

Expected result

I've used the Chrome DevTools to try to figure out what's happening and I think the problem is that the 'hidden' class is not set on the "outermost event element" as the fullCalendar states, but to a inner one:

enter image description here

(first DIV is the first event and as you can see the hidden class is set but not to the DIV, but to the a tag)

Here is a codepen to test it.

IMHO this is a fullCalendar bug, but now I have a problem and I need a solution. My options are:

  1. Use a CSS selector for the parent <-- IMPOSSIBLE: it doesn't exist (yet)
  2. Do (1) using jquery <-- I DON'T KNOW HOW? I know I need to execute something like $(".fc-event.hidden").parent().remove() when the events are loaded and showed but since v3 nothing like this exists UPDATE and even if it exist, with the current v5 removing the DOM element doesn't resize the other event boxes.
  3. Try to repair the code of the library <-- PROBLEMATIC: I don't want to worry about the patch if the next version of the library comes without a solution
  4. Filter events on load <-- SLOW: I use a callback function to load events through Ajax and I can do a fast filtering there, but in this case, I'll lose performance since I'll have to refetch events every time I need to show events with real_rc != 1
  5. Custom views <-- I DON'T WANT TO REINVENT THE WHEEL. As suggested by @saqibkafeel in a comment custom views can be used to create a new view, but I like current views and don't really need a new one, just the default views working as expected.

Is there a way to circumvent this problem without creating a new one? (I feel that the easiest option is to find a hook that allows me do the option number 2, but I have spent all the day and I haven't found anything).


Solution

  • As you already noticed, CSS and JS cannot help here because the events are placed using position:absolute so removing one event (even completely from the DOM) won't affect the display of the other ones. The only way is to remove the event form the calender before rendring.

    So remove the event instead of adding a class:

    eventClassNames: function(arg) { /* you can also use "eventDidMount" */
           if (arg.view.type != 'resourceTimeGridDay') {
              if (arg.event.extendedProps.real_rc != "1") {
                 arg.event.remove(); /* HERE */
              }
           }
        },
    

    Full code:

    document.addEventListener('DOMContentLoaded', function() {
      var calendarEl = document.getElementById('calendar');
    
      var calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: 'timeGridWeek',
        initialDate: '2020-12-22',
        headerToolbar: {
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth,timeGridWeek,timeGridDay'
        },
        height: 'auto',
    
        eventClassNames: function(arg) {
           if (arg.view.type != 'resourceTimeGridDay') {
              if (arg.event.extendedProps.real_rc != "1") {
                 arg.event.remove();
              }
           }
        },
    
        events: [
          {
            title: 'Test Resource 1',
            real_rc: '1',
            start: '2020-12-22 13:00',
            end: '2020-12-22 14:00'
          },
          {
            title: 'Also resource 1',
            real_rc: '1',
            start: '2020-12-22 13:30',
            end: '2020-12-22 14:30'
          },
          {
            title: 'Resource 1',
            real_rc: '1',
            start: '2020-12-22 16:00',
            end: '2020-12-22 17:00'
          },
          {
            title: 'Resource 2',
            real_rc: '2',
            start: '2020-12-22 15:00',
            end: '2020-12-22 17:00'
          }
        ]
      });
    
      calendar.render();
    });
    html, body {
      margin: 0;
      padding: 0;
      font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
      font-size: 14px;
    }
    
    #calendar {
      max-width: 1100px;
      margin: 40px auto;
    }
    <link rel="stylesheet" href="https://unpkg.com/fullcalendar@5.1.0/main.min.css">
    <script src="https://unpkg.com/fullcalendar@5.1.0/main.min.js"></script>
    <div id='calendar'></div>

    Another idea is to control the display of the event like below:

    document.addEventListener('DOMContentLoaded', function() {
      var calendarEl = document.getElementById('calendar');
    
      var calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: 'timeGridWeek',
        initialDate: '2020-12-22',
        headerToolbar: {
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth,timeGridWeek,timeGridDay'
        },
        height: 'auto',
    
        eventDidMount: function(arg) {
           if (arg.view.type != 'resourceTimeGridDay') {
              if (arg.event.extendedProps.real_rc != "1") {
                 arg.event.setProp( 'display', 'none' );
              }
           }
        },
    
        events: [
          {
            title: 'Test Resource 1',
            real_rc: '1',
            start: '2020-12-22 13:00',
            end: '2020-12-22 14:00'
          },
          {
            title: 'Also resource 1',
            real_rc: '1',
            start: '2020-12-22 13:30',
            end: '2020-12-22 14:30'
          },
          {
            title: 'Resource 1',
            real_rc: '1',
            start: '2020-12-22 16:00',
            end: '2020-12-22 17:00'
          },
          {
            title: 'Resource 2',
            real_rc: '2',
            start: '2020-12-22 15:00',
            end: '2020-12-22 17:00'
          }
        ]
      });
    
      calendar.render();
    });
    html, body {
      margin: 0;
      padding: 0;
      font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
      font-size: 14px;
    }
    
    #calendar {
      max-width: 1100px;
      margin: 40px auto;
    }
    <link rel="stylesheet" href="https://unpkg.com/fullcalendar@5.1.0/main.min.css">
    <script src="https://unpkg.com/fullcalendar@5.1.0/main.min.js"></script>
    <div id='calendar'></div>


    An interactive demo where you can toggle display:

    var rc = "1";
    
    document.addEventListener('DOMContentLoaded', function() {
      var calendarEl = document.getElementById('calendar');
    
      var calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: 'timeGridWeek',
        initialDate: '2020-12-22',
        headerToolbar: {
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth,timeGridWeek,timeGridDay'
        },
        height: 'auto',
    
        eventDidMount: function(arg) {
           if (arg.view.type != 'resourceTimeGridDay') {
              if (arg.event.extendedProps.real_rc != rc) {
                 arg.event.setProp( 'display', 'none' );
              } 
           }
        },
        viewDidMount: function(arg) {
           var es = calendar.getEvents();
           for(var i=0;i<es.length;i++)
            es[i].setProp( 'display', 'auto' )
        },
    
        events: [
          {
            title: 'Test Resource 1',
            real_rc: '1',
            start: '2020-12-22 13:00',
            end: '2020-12-22 14:00'
          },
          {
            title: 'Also resource 1',
            real_rc: '1',
            start: '2020-12-22 13:30',
            end: '2020-12-22 14:30'
          },
          {
            title: 'Resource 1',
            real_rc: '1',
            start: '2020-12-22 16:00',
            end: '2020-12-22 17:00'
          },
          {
            title: 'Resource 2',
            real_rc: '2',
            start: '2020-12-22 15:00',
            end: '2020-12-22 17:00'
          }
        ]
      });
    
      calendar.render();
      document.querySelector("#toggle").addEventListener('click',function() {
        if (rc=="1") rc="2"; else rc = "1";
        /* trigger view change */
        calendar.changeView('dayGridMonth');
        calendar.changeView('timeGridWeek');
      });
    });
    html, body {
      margin: 0;
      padding: 0;
      font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
      font-size: 14px;
    }
    
    #calendar {
      max-width: 1100px;
      margin: 40px auto;
    }
    <link rel="stylesheet" href="https://unpkg.com/fullcalendar@5.1.0/main.min.css">
    <script src="https://unpkg.com/fullcalendar@5.1.0/main.min.js"></script>
    <button id="toggle">toggle</button>
    <div id='calendar'></div>