Skip to content

Google Calendar-like Event Filtering with jQuery and the FullCalendar Plugin.

October 4, 2009

Recently, I had a need to create a calendar that supported filtering and always thought one of the cool features of Google Calendar is the Other calendars filtering–the ability to decide which events/calendars to view. This functionality is very easy to mimic using jQuery and the FullCalendar plugin.

Once you have the FullCalendar plugin, it’s very simple to set up. We’ll start with the HTML snippet:

<div id="calendar"></div>
<div id="filtering">
  <h2>Filtering</h2>
  <ul>
    <li class="special item-selected item-selected-special">Special Events</li>
    <li class="regular item-selected item-selected-regular">Regular Events</li>
  </ul>
</div>

Of course, it’s possible that you’ll have a more dynamic way of generating the list of event types. The point is that you need an unordered list and each list item will use one or three classes:

  1. The class that represents the event type. It’s not used for styling, but its purpose will become clear later on.
  2. A class item-selected which represents the common style among selected list items.
  3. A class item-selected-[event type] which handles the specific styling for each event type when selected.

If you don’t want an event type to show up on the calendar upon initial load, just use the first class, leave out the selected classes.

Moving on to the CSS:

@import "fullcalendar-1.3/fullcalendar.css";
body {
  margin: 1em;
}
/* Calendar container */
#calendar {
  float: left;
  margin-left: 1em;
  width: 80%;
}
/*Filter container */
#filtering {
  float: right;
  width: 15%;
}
/* Filter title */
#filtering h2 {
  margin-top: 0;
}
/* Filter item container */
#filtering ul {
  list-style: none;
  margin: .5em 0;
  padding: 0;
}
/* Filter item */
#filtering li {
  background-color: #FFF;
  border-color: #FFF;
  border-style: solid;
  border-width: 1px;
  color: #666;
  cursor: pointer;
  margin: .2em 0;
  -moz-border-radius: 5px;
  padding: .2em;
  text-decoration: none;
}
/* Hover filter item */
#filtering li.item-hover {
  text-decoration: underline;
}
/* Hover regular events filter item */
#filtering li.item-hover-regular {
  background: #FEE !important;
  border-color: #F99;
  color: #F99 !important;
}
/* Hover special events filter item */
#filtering li.item-hover-special {
  background: #EEE !important;
  border-color: #CCC;
  color: #777 !important;
}
/* Selected filter item */
#filtering li.item-selected {
  color: #FFF;
}
/* Selected regular events */
#filtering li.item-selected-regular, #calendar .regular, #calendar .regular a {
  background-color: #A01;
  border-color: #A01 !important;
}
/* Selected special events */
#filtering li.item-selected-special, #calendar .special, #calendar .special a {
  background-color: #777;
  border-color: #777 !important;
}

For demonstration purposes, the overall layout is very basic—calendar to the left, filtering to the right. The idea is to illustrate how each event type has its own unique coloring while maintaining a common look and feel between the calendar and the filter item. With jQuery, these styles will be used with event binding and give us the general functionality of Google Calendar filtering.

Finally, the JavaScript entails defining a couple of functions, populating an array of events through some method, probably dynamically, and then some event binding. To start, obviously you’ll need to load the jQuery library and the FullCalendar plugin in some manner.

<script type="text/javascript" src="jquery.min.js">/script>
<script type="text/javascript" src="fullcalendar.min.js"></script>

Next you’ll need to populate the array that holds the events to be shown on the calendar. In this case, I’ve just hard-coded a few, but this information is more likely to come from an actual data source. The key here is to give the className property of each event the same value as the first class name given to its respective filtering item.

var  d = new Date();
var  y = d.getFullYear();
var  m = d.getMonth();
var events =
  [
    {
      id: 1,
      title: "Special Event",
      start: new Date(y, m, 6),
      end: new Date(y, m, 11),
      className: "special"
    },
    {
      id: 2,
      title: "Regular Repeating Event",
      start: new Date(y, m - 1, 2),
      className: "regular"
    },
    {
      id: 3,
      title: "Special Repeating Event",
      start: new Date(y, m, 9),
      className: "special"
    },
    {
      id: 4,
      title: "Special Meeting",
      start: new Date(y, m, 20, 9, 0),
      allDay: false,
      className: "special"
    },
    {
      id: 5,
      title: "Regular 2-day",
      start: new Date(y, m, 27),
      end: new Date(y, m, 29),
      allDay: false,
      className: "regular"
    },
    {
      id: 6,
      title: "Special Meeting",
      start: new Date(y, m - 1, 16, 9, 0),
      allDay: false,
      className: "special"
    },
    {
      id: 7,
      title: "Regular Repeating Event",
      start: new Date(y, m + 1, 9),
      className: "regular"
    },
  ]

To reduce code redundancies, there are a couple of helper functions, getBaseClassName which will return the class name used to match a filtering item to the event items and applyFilter which fires every time the calendar view changes and allows us to maintain filtering state.

var getBaseClassName = function(className) {
  return className.split(" ")[0];
};

var applyFilter = function(view) {
  jQuery("#filtering li").each(
    function(i) {
      if (jQuery(this).attr("class").indexOf("selected") == -1)
        jQuery("div." + getBaseClassName(jQuery(this).attr("class"))).hide();
    }
  )
};

With that out of the way, we are ready to work with the infamous jQuery(document).ready() function. Inside of the function passed into ready() the first task is to initialize the calendar.

jQuery("#calendar").fullCalendar({
  editable: false,
  viewDisplay: applyFilter,
  events: events
});

Next is a visual aid to align the filtering area with the top of the calendar rather than the top of the calendar container:

jQuery("#filtering").css("marginTop", jQuery("#calendar div.fc-widget-content").offset().top);

Lastly, the event binding that puts it all together:

jQuery("#filtering li")
  .toggle(
    function() { 
      var baseClassName = getBaseClassName(jQuery(this).attr("class"));
      jQuery("div." + baseClassName).toggle();
      jQuery(this).removeClass("item-selected item-selected-" + baseClassName).removeClass("item-hover item-hover-" + baseClassName);
    },
    function() {
      var baseClassName = getBaseClassName(jQuery(this).attr("class"));
      jQuery("div." + baseClassName).toggle();
      jQuery(this).addClass("item-selected item-selected-" + baseClassName).removeClass("item-hover item-hover-" + baseClassName);
    }
  )
  .hover(
    function() { 
      jQuery(this).addClass("item-hover item-hover-" + getBaseClassName(jQuery(this).attr("class")));
    },
    function() { 
      jQuery(this).removeClass("item-hover item-hover-" + getBaseClassName(jQuery(this).attr("class")));
    }
  )
About these ads

From → jQuery

2 Comments
  1. Nice one, this really helped me a lot

  2. Terrific write-up! The only issue that I had in implementation was that I was using Google Calendar to feed my events, and the “jQuery(“div.” + baseClassName).toggle();” in the last code block wasn’t selecting my events properly. In my case I hade to change “div.” to “a.fc-event.fc-event-skin.fc-event-hori.fc-corner-left.fc-corner-right.” because that’s how Google passed the events along. Again, thank you for the amazing write-up!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: