TIP/Trick: Retrieving dates with the jQuery UI DatePicker

jQuery UI DatePicker

jQuery UI extends the underlying jQuery library to provide a suite of rich and interactive widgets along with code-saving interaction helpers, built to enhance the user interfaces of your websites and web applications.

Quite simply, the datepicker widget provides an interface that allows visitors of your site or application to select dates. Wherever a form field is required that asks for a date to be entered, the datepicker widget can be added.

The datepicker is tied to a standard form input field. Focus on the input (click, or use the tab key) to open an interactive calendar in a small overlay. Choose a date, click elsewhere on the page (blur the input), or hit the Esc key to close. If a date is chosen, feedback is shown as the input's value.

The Problem

jQuery UI widgets are often set via an HTML attribute selector. The options are set in just one place and applied to all our date fields accordingly.

This allows us to obtain a uniform date selection behavior.

<input type="text" id="dateOfBirth" class="datePicker" />
<input type="text" id="from" class="datePicker" />
<input type="text" id="to" class="datePicker" />
<script>
    // set the date pickers.
    $('.datePicker').datepicker();
</script>

The challenge comes when we try to retrieve the selected date and do something with the value.

I have summarized some common tricky scenarios that I have come across while working with dates using a client side widget:

  • Parsing date strings is hard, specially when we support localization. Let's face it, handling dates is hard. Support for localized date times or custom date format representations are just a couple of common scenarios that add complexity to the manipulation of the selected values using the DatePicker. It is ideal to use a date object in order to ignore to handle data in a more secure and clean manner.

  • jQuery UI DatePicker callback definition happens at the widget binding step. We could set it individually, per date picker instance, but then we would lose the flexibility we gain by using an attribute to set multiple fields at the same time.

  • It's not intuitive that the 'change' event of the input gets triggered when a date is selected. Also, it only works using the jQuery event model.

Retrieving the date

Instinctively, we retrieve dates, set using the DatePicker widget, from the value in the input field binded.

Example:

// Set the date picker
//
$("#date").datepicker();

// Retrieve the value of the input field
//
$("#date").val();

Parsing dates as text is a complicated task. Localized dates and custom text formats make everything more difficult. The massive amount of edge cases involved make JavaScript's default date parsing tools inadequate for these scenarios.

The best way of handling dates is doing it atomically. This means, that you can retrieve each part of the date with ease as a single unit of data. Then, put it together however you need depending on your date formatting rules.

For this purposes, the DatePicker widget provides a helper function that returns a JavaScript date object.

// Retrieve the selected date as a date object
//
var date = $("#date").datepicker('getDate');

// Returns the day of the month (from 1-31)
//
var dayOfTheWeek = date.getDay(); 

// Returns the day of the week (from 0-6)
//
var dayOfTheMonth = date.getDate(); 

// Returns the month (from 0-11)
//
var month = date.getMonth(); 

// Returns the year (four digits)   
//
var year = date.getFullYear(); 

This approach provides us with flexibility. Each part of the date can be retrieved and evaluated without having to take the parsing into our own hands. You can read more about the JavaScript date object here.

Heads up: jQuery Events != JavaScript Events

jQuery is wrongly described as a major component of the HTML5 revolution. Do not get me wrong, jQuery is compliant with the latest Web standards, such as, CSS3 and (of course) HTML5. However, its purpose is not to be a cornerstone of a new Web standard, but converge several standards (old and new) into an uniform experience regardless of the browser you use.

Both jQuery Core and UI are built according to strict coding conventions, which are updated regularly, and follow the current best practice for JavaScript design. As the official UI library for jQuery, it's this strict adherence to current JavaScript standards that helps to make it one of the better UI libraries available for jQuery.

JavaScript event handling is the basis of all client-side applications and as discussed in a previous article, UI programmers had to jump through many hurdles in order to achieve cross-browser compatibility when implementing event handling.

jQuery Events

jQuery's event module provides a uniform cross-browser event handling experience. To achieve this, jQuery encapsulates browser-specific fixes to normalize behavior in easy to use event subscription interfaces and helpers.

// jQuery 1.7+ Event Handler Attachment functions
// http://api.jquery.com/category/events/event-handler-attachment/
//
.on( events [, selector ] [, data ], handler )
.off( events [, selector ] [, handler ] )
.trigger( eventType [, extraParameters ] )

Regardless of the similarities between jQuery's event handling functions and the DOM Level 2 Event Model, both approaches cannot be used interchangeably. The way events are triggered and handled are incompatible. I would recommend sticking to the jQuery's event handling system if you are already using the library.

jQuery UI DatePicker Events

Interactions with the DatePicker widget trigger only one event: input's 'change' event. This allows us to react after a date is selected.

In order to ensure compatibility with old browsers, the jQuery UI DatePicker uses the jQuery event model.

Example:

// jQuery 1.7+
$( document ).on( "change", "#date", function(event) { 
      // Do stuff with the captured jQuery event.
    //

    // Perhaps also get the date object?
    //
    var date = $("#date").datepicker('getDate');
});

jQuery UI DatePicker Callbacks

The jQuery UI DatePicker widget exposes actions which can be set through callback functions. Similar to other HTML elements, these callback functions can be used to specify the code to be executed at different points during interaction with the datepicker.

The widget exposes five different callbacks:

  • beforeShow: The datepicker is about to open.
  • beforeShowDay: Each individual date is rendered in the datepicker. Can be used to determine whether the date should be selectable or not.
  • onChangeMonthYear: The current month or year changes.
  • onClose: The datepicker is closed.
  • onSelect: A date is selected.

Example:

$(document).ready(function() {
    $('#callbacks').datepicker({
        beforeShow: function(input, inst) {
            // beforeShow!
            // http://api.jqueryui.com/datepicker/#option-beforeShow
        },
        beforeShowDay: function(date) {
            // beforeShowDay
            // http://api.jqueryui.com/datepicker/#option-beforeShowDay
        },
        onChangeMonthYear: function(year, month, inst) {
            // onChangeMonthYear
            // http://api.jqueryui.com/datepicker/#option-onChangeMonthYear
        },
        onClose: function(dateText, inst) {
            // onClose
            // http://api.jqueryui.com/datepicker/#option-onClose
        },
        onSelect: function(dateText, inst) {
            // onSelect
            // http://api.jqueryui.com/datepicker/#option-onSelect
        }
    });
});

Late binding to the jQuery UI DatePicker's callbacks

The main issue with the callbacks is that they are defined upon the widget binding. Therefore, if we are using a single attribute binding call, adding specific behavior per widget becomes a challenge.

In order to solve this we have two different options:

  • Retrieve a specific widget instance and override the callback
  • Create a custom event for all binded widgets

The first approach is ideal if we want to override the behavior of specific widget instances. A drawback is that we rely on a previous widget binding on the selected input field.

Example:

$(".date").datepicker({
    onSelect: function(){
        // Do nothing.
    }
});

$( "#example4" ).datepicker( "option", "onSelect", function(){
     // Do something!
});

The second approach is ideal for a more generic scenario. We create a custom event that gets dispatched everytime the callback is triggered. This way we have the option of handling the callback as it were a regular JavaScript event.

Example:

$(".date").datepicker({
    onSelect: function (dateText, inst) {
        var onSelectEvent = new CustomEvent('onCustomSelect',
            {
                'detail': {
                    id: this.id,
                    dateText: dateText,
                    instance: inst
                },
                bubbles: true,
                cancelable: true
            });
        this.dispatchEvent(onSelectEvent);
    }
});

// onCustomSelect event subscription
//
$(document).on( "onCustomSelect", ".date", function(event) {
    // The event argument has lots of info so we don't have 
    // to request the instance again and again using jQuery.

    // For example the date object can be retrieved easily.
    //
    var selectedDate = event.originalEvent.detail.instance;
});

Personally, I find this useful since it reduces the amount of times the widget instance is retrieved for things like retrieving the selected date object.

Conclusion

User interface libraries like jQuery UI provide widgets that seamlessly integrate with each other, delivering a consistent, high quality look and feel.

The DatePicker solves a common need that at some point every Web developer has had. This is why its API is the most extense and complete among the other options in jQuery UI.

You might want to take a look to the book jQuery UI 1.10: The User Interface Library for jQuery where the author goes all in into jQuery UI's architecture and widgets.

Happy coding!