I am also stuck with this problem, it is very difficult, because the plugin presents the calendar in an odd way, using some tables and dynamically determining events with an absolute position and changing the css top property.
However, I found a general solution that works very well. First I will show you the code and they will explain what exactly the code does.
I use the eventAfterAllRender option of the full calendar. This is an example of work . 
I am using moment to control the time, and I am assuming that the identifier of the fullCalendar html element is Calendar .
eventAfterAllRender: function() { // define static values, use this values to vary the event item height var defaultItemHeight = 25; var defaultEventItemHeight = 18; // ... // find all rows and define a function to select one row with an specific time var rows = []; $('div.fc-slats > table > tbody > tr[data-time]').each(function() { rows.push($(this)); }); var rowIndex = 0; var getRowElement = function(time) { while (rowIndex < rows.length && moment(rows[rowIndex].attr('data-time'), ['HH:mm:ss']) <= time) { rowIndex++; } var selectedIndex = rowIndex - 1; return selectedIndex >= 0 ? rows[selectedIndex] : null; }; // reorder events items position and increment row height when is necessary $('div.fc-content-col > div.fc-event-container').each(function() { // for-each week column var accumulator = 0; var previousRowElement = null; $(this).find('> a.fc-time-grid-event.fc-v-event.fc-event.fc-start.fc-end').each(function() { // for-each event on week column // select the current event time and its row var currentEventTime = moment($(this).find('> div.fc-content > div.fc-time').attr('data-full'), ['h:mm A']); var currentEventRowElement = getRowElement(currentEventTime); // the current row has to more than one item if (currentEventRowElement === previousRowElement) { accumulator++; // move down the event (with margin-top prop. IT HAS TO BE THAT PROPERTY TO AVOID CONFLICTS WITH FullCalendar BEHAVIOR) $(this).css('margin-top', '+=' + (accumulator * defaultItemHeight).toString() + 'px'); // increse the heigth of current row if it overcome its current max-items var maxItemsOnRow = currentEventRowElement.attr('data-max-items') || 1; if (accumulator >= maxItemsOnRow) { currentEventRowElement.attr('data-max-items', accumulator + 1); currentEventRowElement.css('height', '+=' + defaultItemHeight.toString() + 'px'); } } else { // reset count rowIndex = 0; accumulator = 0; } // set default styles for event item and update previosRow $(this).css('left', '0'); $(this).css('right', '7px'); $(this).css('height', defaultEventItemHeight.toString() + 'px'); $(this).css('margin-right', '0'); previousRowElement = currentEventRowElement; }); }); // this is used for re-paint the calendar $('#calendar').fullCalendar('option', 'aspectRatio', $('#calendar').fullCalendar('option', 'aspectRatio')); }
How the code works:
First I found all the tr elements that are the rows of my calendar (note that they contain an attribute with their time).
Later, I repeat for each column and get event elements for each column. Each event element is a binding element with some internal child element with a date as a filled attribute.
From the event I look at what its line should be, and in this line, if there is more than one element, then increase the position in which the event element should be. I use the margin-top property for this, because this property is not used or is not adjusted by the plugin (do not use the top property).
In the row, I set the data attribute to accept the maximum number of events that have any column in this row. In doing so, I can calculate whether the row should increase its height or not.
Well, thatβs basically what codes do. If you have a question, please do so.