How To Add Full Calendar Icon To Monthly View Grid?

With And Without dayCellDidMount hook

To add an icon to a day, we need to identify the grid we want to add an icon to. For instance, today, past days, future days, weekends only etc. Then we add the icon through JavaScript.

There are two ways to do that:

  1. Identify the cell to add icon to inside dayCellDidMount render hook, and add the icon
  2. Right After calendar render, select the cell element(s) through the combination of classes using querySelectorAll and add icon to it/them

Here’s the calendar with plus icon in all future days.

Here’s how to add it, using both methods.

Using dayCellDidMount

dayCellDidMount render hook gives us arg object, which has el in it that we can use to manipulate or add to the required cell.

document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');
    var calendar = new FullCalendar.Calendar(calendarEl, {
      initialView: 'dayGridMonth',
      dayCellDidMount : function(arg){
        if(arg.el.classList.contains("fc-day-future")){
          var theElement = arg.el.querySelectorAll(".fc-daygrid-day-frame > .fc-daygrid-day-events")[0]
          setTimeout(function(){
            if(theElement.querySelectorAll(".fc-daygrid-event-harness").length === 0){ // check there's no event
              theElement.innerHTML = theElement.innerHTML + '<div class="text-center"><i class="calendar-icon fas fa-plus"></i></span></div>';
            }
          }, 10)
        }
      }
    });
    calendar.render();  
  });

In case of events present in any of the day, we don’t want to show plus icon. For that we check the existence of .fc-daygrid-event-harness class inside the element, because it won’t be present in event-less days. Notice that this check is wrapped in setTimeout. It’s because .fc-daygrid-event-harness is always empty initially at the time dayCellDidMount is called.

Using querySelectorAll

The same thing as above can be done by identifying all the elements using document.querySelectorAll without using any hook.

document.addEventListener('DOMContentLoaded', function() {
  var calendarEl = document.getElementById('calendar');
  var calendar = new FullCalendar.Calendar(calendarEl, {
    initialView: 'dayGridMonth'
  });
  calendar.render();

  var selectedDays = document.querySelectorAll('.fc-day-future > .fc-daygrid-day-frame > .fc-daygrid-day-events');
  for(var i = 0; i < selectedDays.length; ++i){
    if(selectedDays[i].querySelectorAll(".fc-daygrid-event-harness").length === 0){ // check there's no event
      selectedDays[i].innerHTML = selectedDays[i].innerHTML + '<div class="text-center"><i class="calendar-icon fas fa-plus"></i></span></div>';
    }
  }
});

We loop over future days by selecting them through .fc-day-future > .fc-daygrid-day-frame > .fc-daygrid-day-events selector.

Notice there’s a downside of this method. We only add icon on initial render. So if we move between months by clicking today, next or prev, the icons won’t appear in future months, and the current icons will vanish too.

To overcome this, we add a listener to .fc-button, which will be fired on clicking today, next and prev, and call addIcon from inside of it. addIcon is a method where we move our original logic. We also call this method once outside click listener for initial rendering.

We also need to add an extra check to look for existing .fa-plus, else multiple icons will appear in some days.

document.addEventListener('DOMContentLoaded', function() {
   var calendarEl = document.getElementById('calendar');
   var calendar = new FullCalendar.Calendar(calendarEl, {
    initialView: 'dayGridMonth'
   });
   calendar.render();

   function addIcon(){
      var selectedDays = document.querySelectorAll('.fc-day-future > .fc-daygrid-day-frame > .fc-daygrid-day-events');
      for(var i = 0; i < selectedDays.length; ++i){
        if(selectedDays[i].querySelectorAll(".fc-daygrid-event-harness").length === 0 &&
        selectedDays[i].querySelectorAll(".fa-plus").length === 0){
          selectedDays[i].innerHTML = selectedDays[i].innerHTML + '<div class="text-center"><i class="calendar-icon fas fa-plus"></i></span></div>';
        }
      }
    }

    document.querySelectorAll('.fc-button').forEach(function(el){
      el.addEventListener('click', function() {
        addIcon()
      });
    }); 

    addIcon();
});

In both cases above, text-center is a bootstrap class to center the icon (Tailwind’s text-center will work the same), and calendar-icon is a class to change icon font and color.

.calendar-icon {
  font-size: 14px;
  color: lightgray;
}

The fontawesome can be replaced with glyphicon icon too:

selectedDays[i].innerHTML = selectedDays[i].innerHTML + 
  '<div class="text-center"><span class="calendar-icon glyphicon glyphicon-plus"></span></div>';

Other Selectors

Below are some useful query selectors examples for conditionally adding icons. That can be replaced in either of the methods mentioned above.

- Mondays

if(arg.el.classList.contains("fc-day-mon")) {
  //...
}

// OR

document.querySelectorAll('.fc-day-mon > .fc-daygrid-day-frame > .fc-daygrid-day-events');

- Future Mondays

if(arg.el.classList.contains("fc-day-mon") && arg.el.classList.contains("fc-day-future")) {
  //...
}

//OR

document.querySelectorAll('.fc-day-mon.fc-day-future > .fc-daygrid-day-frame > .fc-daygrid-day-events');

- Past Mondays

if(arg.el.classList.contains("fc-day-mon") && arg.el.classList.contains("fc-day-past")) {
  //...
}

// OR

document.querySelectorAll('.fc-day-mon.fc-day-past > .fc-daygrid-day-frame > .fc-daygrid-day-events');

- Weekends

if(arg.el.classList.contains("fc-day-sun") || arg.el.classList.contains("fc-day-sun")) {
  //...
}

// OR

document.querySelectorAll('.fc-day-sun,fc-day-sat > .fc-daygrid-day-frame > .fc-daygrid-day-events');

Add Multiple Icons On Different Days

For this we need to get select multiple elements with different conditions and add our icons of choice. Using the second method, let’s add a cross icon on past days, and plus on future days.

The code would be:

  document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');
    var calendar = new FullCalendar.Calendar(calendarEl, {
      initialView: 'dayGridMonth'
    });
    calendar.render();

    function addIcon() {
      var futureDays = document.querySelectorAll('.fc-day-future > .fc-daygrid-day-frame > .fc-daygrid-day-events');
      for (var i = 0; i < futureDays.length; ++i) {
        if (futureDays[i].querySelectorAll(".fc-daygrid-event-harness").length === 0 &&
          futureDays[i].querySelectorAll(".fa-plus").length === 0) {
          futureDays[i].innerHTML = futureDays[i].innerHTML + '<div class="text-center"><i class="calendar-icon fas fa-plus"></i></span></div>';
        }
      }

      var pastDays = document.querySelectorAll('.fc-day-past > .fc-daygrid-day-frame > .fc-daygrid-day-events');
      for (var i = 0; i < pastDays.length; ++i) {
        if (pastDays[i].querySelectorAll(".fc-daygrid-event-harness").length === 0 &&
          pastDays[i].querySelectorAll(".fa-times").length === 0) {
          pastDays[i].innerHTML = pastDays[i].innerHTML + '<div class="text-center"><i class="calendar-icon fas fa-times"></i></span></div>';
        }
      }
    }

    document.querySelectorAll('.fc-button').forEach(function(el){
      el.addEventListener('click', function() {
        addIcon()
      });
    }); 

    addIcon();
  });