JQuery and the multiple date selector
- by David Carter
Overview
I recently needed to build a web page that would allow a user to capture some information and most importantly select multiple dates. This functionality was core to the application and hence had to be easy and quick to do. This is a public facing website so it had to be intuitive and very responsive.
On the face of it it didn't seem too hard, I know enough juery to know what it is capable of and I was pretty sure that there would be some plugins that would help speed things along the way. I'm using ASP.Net MVC for this project as I really like the control that it gives you over the generated html and javascript. After years of Web Forms development it makes me feel like a web developer again and puts a smile on my face, that can only be a good thing!
The Calendar
The first item that I needed on this page was a calender and I wanted the ability to:
have the calendar be always visible
select/deselect multiple dates at the same time
bind to the select/deselect event so that I could update a seperate listing of the selected dates
allow the user to move to another month and still have the calender remember any dates in the previous month
I was hoping that there was a jQuery plugin that would meet my requirements and luckily there was! The jQuery datepicker does everything I want and there is quite a bit of documentation on how to use it. It makes use of a javascript date library date.js which I had not come across before but has a number of very useful date utilities that I have used elsewhere in the project.
As you can see from the image there still needs to be some styling done! But there will be plenty of time for that later. The calendar clearly shows which dates the user has selected in red and i also make use of an unordered list to show the the selected dates so the user can always clearly see what has been selected even if they move to another month on the calendar. The javascript code that is responsible for listening to events on the calendar and synchronising the list look as follows:
<script type="text/javascript">
$(function () {
$('.datepicker').datePicker({ inline: true, selectMultiple: true })
.bind(
'dateSelected',
function (e, selectedDate, $td, state) {
var dateInMillisecs = selectedDate.valueOf();
if (state) { //adding a date
var newDate = new Date(selectedDate);
//insert the new item into the correct place in the list
var listitems = $('#dateList').children('li').get();
var liToAdd = "<li id='" + dateInMillisecs + "' >" + newDate.toString('ddd dd MMM yyyy') + "</li>";
var targetIndex = -1;
for (var i = 0; i < listitems.length; i++) {
if (dateInMillisecs <= listitems[i].id) {
targetIndex = i;
break;
}
}
if (targetIndex < 0) {
$('#dateList').append(liToAdd);
}
else {
$($('#dateList').children("li")[targetIndex]).before(liToAdd);
}
}
else {//removing a date
$('ul #' + dateInMillisecs).remove();
}
}
)
});
When a date is selected on the calendar a function is called with a number of parameters passed to it. The ones I am particularly interested in are selectedDate and state. State tells me whether the user has selected or deselected the date passed in the selectedDate parameter. The <ul> that I am using to show the date has an id of dateList and this is what I will be adding and removing <li> items from.
To make things a little more logical for the user I decided that the date should be sorted in chronological order, this means that each time a new date is selected it need to be placed in the correct position in the list. One way to do this would be just to append a new <li> to the list and then sort the whole list. However the approach I took was to get an array of all the items in the list
var listitems = ('#dateList').children('li').get();
and then check the value of each item in the array against my new date and as soon as I found the case where the new date was less than the current item remember that position in the list as this is where I would insert it later.
To make this work easily I decided to store a numeric representation of each date in the list in the id attribute of each <li> element. Fortunately javascript natively stores dates as the number of milliseconds since 1 Jan 1970.
var dateInMillisecs = selectedDate.valueOf();
Please note that this is the value of the date in UTC! I always like to store dates in UTC as I learnt a long time ago that it saves a lot of refactoring at a later date... When I convert the dates back to their original back on the server I will need the UTC offset that was used when calculating the dates, this and how to actually serialise the dates and get them posted back will be the subject of another post.