In some scenarios, you might want to add extended client behavior to a Calendar View in SharePoint 2010. The client-side API of the Calendar View ist not of a great help in every case here. especially, it does not provide an event or hook to catch clicks on calendar items.

Fortunately, JavaScript itself is powerful enough to allow us injecting such hooks by ourselves. The trick is just to override the API functionSP.UI.ApplicationPages.CalendarContainerFactory.create. We just need to wait until the calendar API is loaded and call a function provided by us:

ExecuteOrDelayUntilScriptLoaded(
    MyCalendarHook, 
    "SP.UI.ApplicationPages.Calendar.js");

Be sure to place this call so that the function MyCalendarHook is called before any other calendar-related functions, especially to any other calls to ExecuteOrDelayUntilScriptLoaded with “SP.UI.ApplicationPages.Calendar.js”. In this function, we override SP.UI.ApplicationPages.CalendarContainerFactory.create with whatever code we need. For example:

function MyCalendarHook() {     
    var _patchCalendar = function() {        
        $('.ms-acal-rootdiv .ms-acal-item').click(function () {
            var itemId = $('a', $(this)).attr('href').split('?ID=')[1];
            alert('Item with ID ' + itemId + ' was clicked!');
        });
    };
    
    var _onItemsSucceed = SP.UI.ApplicationPages.CalendarStateHandler.prototype.onItemsSucceed;
    SP.UI.ApplicationPages.CalendarStateHandler.prototype.onItemsSucceed = function($p0, $p1) {
        _onItemsSucceed.call(this, $p0, $p1);
        _patchCalendar();
    };
}

In the code above, we override a function that is called whenever calendar items are loaded (onItemsSucceed). Inside our own onItemsSucceed, we call the original onItemsSucceed to make sure that all calendar functionality is as it used to be. After that, we patch the calendar: find each calendar item (i.e. appointment) and add an onClick event handler. Inside the handler, we can do whatever we want. Note that we’re using jQuery in the example above for simplicity, but you’ll need make sure that jQuery is referenced (or use plain JavaScript instead).

Where to put this?

To show where the scripts from above can be included, below is the header element from the master file V4.master I used. The only changes made to this file are the two script-includes between the “BEGIN DEMO” and “END DEMO” comments. The file patchcalendar.js contains exactly the two script blocks from above. Nevermind the jQuery version – this should work with any version.

<head runat="server">
  <meta http-equiv="X-UA-Compatible" content="IE=8"/>
  <meta name="GENERATOR" content="Microsoft SharePoint"/>
  <meta name="progid" content="SharePoint.WebPartPage.Document"/>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <meta http-equiv="Expires" content="0"/>
  <SharePoint:RobotsMetaTag runat="server"/>
  <title id="onetidTitle"><asp:ContentPlaceHolder id="PlaceHolderPageTitle" runat="server"/></title>
  <SharePoint:CssLink runat="server" Version="4"/>
  <SharePoint:Theme runat="server"/>
  <SharePoint:ULSClientConfig runat="server"/>
  <script type="text/javascript">
  var _fV4UI = true;
  </script>
  <SharePoint:ScriptLink language="javascript" name="core.js" OnDemand="true" runat="server"/>
  <SharePoint:CustomJSUrl runat="server"/>
  <SharePoint:SoapDiscoveryLink runat="server"/>
  <asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server"/>
  <SharePoint:DelegateControl runat="server" ControlId="AdditionalPageHead" AllowMultipleControls="true"/>
  <SharePoint:SPShortcutIcon runat="server" IconUrl="/_layouts/images/favicon.ico"/>
  <asp:ContentPlaceHolder id="PlaceHolderBodyAreaClass" runat="server"/>
  <!-- BEGIN DEMO -->
  <script type="text/javascript" src="/_layouts/js/jquery-1.6.1.js"></script>
  <script type="text/javascript" src="/_layouts/js/patchcalendar.js"></script>
  <!-- END DEMO -->
  <asp:ContentPlaceHolder id="PlaceHolderTitleAreaClass" runat="server"/>
  <SharePoint:SPPageManager runat="server"/>
  <SharePoint:SPHelpPageComponent Visible="false" runat="server"/>
</head>

You see, even if some product’s API does not allow us to do everything we like, the power of JavaScript does 🙂