Revisiting the JavaScript Calendar Control – Part 2

Show your scripts who’s the boss! Check out The Little Book of JavaScript!

Following last week’s implementation of the basic shape of the calendar, this week we will add behaviours so that we can pick a date on it.

This post is part of a series of three posts:

  • In part 1 we created a jQuery plugin which displays a simple static calendar.
  • In part 2 (this post), we will add the facility to pick a date, and to switch the month on display.
  • In part 3, we will add some sanity checks, and the ability to display events on given days.

Changes from Part 1

The source code has been largely refactored since last week. While little new functionality was added, we moved a lot of code around to make it more manageable. For example, the part which populates the body has been moved to a separate method, since it can now be called from more than one point – either when the calendar is being created, or when the month on display changes.

If you look at the source, you will see that a new data entry has been added – the date.active value. This value will be used to tell the plugin which month to display. Although we could have used the selected date for this, I do not find that solution to be completely usable – the original script works like that, and it does not allow you to switch to different months without changing the selected date. This change was very simple to apply, as it only required changing which data element the rendering uses.

The most significant change is the addition of the selection and date change behaviour, which we shall now look at in more detail.

You can see the plugin in action here, or download the javascript source here.

Adding Behaviours

Adding behaviours in jQuery is no different to adding a class, or manipulating a property. In other words, we grab a number of elements we want to affect, in our case the cells in the table, and give them an event handler:

163: jQuery("td", table).click(selectCell);

where selectCell is a function which takes no parameters and returns nothing. We’re re-binding the on click every time the calendar is repopulated. While this isn’t necessary now – we could just leave the same function bound – we’re going to need to do this in the next step when we add sanity checks, so we might as well leave it there now.

Since we’re rebinding the function, we’re also unbinding it at line 115, so it doesn’t get bound multiple times. Cleaning up after yourself and unbinding functions you don’t need is a good habit to get into, both to keep the javascript engine happy, and to save yourself some hair loss due to having multiple handlers on the same event.

Another point to note is that we’re using a named function here rather than creating a new anonymous function. That is, we’re not doing

163: jQuery("td", table).click(function() { … });

This would create a new instance of the function every time, which would consume resources unnecessarily.

So what happens when a cell is clicked? As we said, the selectCell function takes no parameters, but since the click happens in the context of a cell, “this” within the event handler refers to the cell. As you may remember from part 1, each cell knows which table it belongs to and which date it represents, and that’s all the information we need. We get the date, assign the date to the table, and tell it to redraw itself.

The behaviour for the back and forward arrows in the caption work similarly, except that we’re changing the date on display now, rather than the selected date.

Interacting with the outside world

Now we have a simple, but fully functional date picker control. One thing is missing though: it can’t really communicate with other controls. That makes it as useful as a rubber duck for the purposes of selecting a date, so we probably should provide it with some means of letting the world around it know when something happens to it.

We can do this by creating callback hooks. These are nothing but placeholders for a function which will be called when an event occurs. If the function exists, then it gets called:

201: if (table.data("onDisplayMonthChanged"))
202:     table.data("onDisplayMonthChanged")(table.data("date.active"));

Two callbacks have been added; one that is called when the active date changes, and one that is called when the selected date changes.

We may also want to interact with the calendar from the outside, for example to pass in the selected date from outside. The simplest way to do this was to modify the calendar function, the same we use to create the calendar, to determine whether the target is an existing calendar or otherwise. If it’s a calendar, it will update the date, otherwise it will create a new one. The test is easy to implement in jQuery:

10: var isNew = !target.data("date.active");
11: if (!target.is("table") || isNew) { … }

The is function tells us whether or not the target object is a table. It can take any selector as a parameter, so it’s very useful for this sort of test. In our case, we decide whether the element is a calendar or otherwise by confirming that it is a table, and that it has the “date.active” data element set.

A Gotcha with UTC Dates

When trying to determine the first day to display in the calendar, I was having some problems due to the UTC time difference. The calculation was originally based off the first day of the month; however, because of time zone differences, this may lead to the previous month being selected instead. To avoid this, we’re now using the second day of the month as our base. The actual day of the month isn’t important, since we’re only concerned with the year and month parts, and by using the second day, we can be sure that it will stay within the correct month regardless of time zone difference.

A Note on Usability

At this stage, I’m a bit concerned over the usability of this script. Not in terms of user experience usability, but, shall we call it coder usability.

The problem is this: When we create a new calendar, and we call the following:

00: $(“#someId”).calendar(…);

Then the plugin will create a calendar just after the element with an id attribute of someId. However, to modify the calendar value, we’d need to use a different selector, for example

00: $(“#someId + table”).calendar(…);

This looks quite counterintutive, so it should probably change for the next part of this series. Do you have any suggestions as to how this could look?

Finally

Adding behaviour turned out to be easier than falling off a chair; most of the changes for this part were actually cleanup. This is a clear example of the difference which good tools (in this case, jQuery) make.

2 Replies to “Revisiting the JavaScript Calendar Control – Part 2”

    1. Coming up as soon as I finish doing a write up for it 😀
      Thanks for your comment, Alex. Hope you found it useful so far.

Comments are closed.