I’ve always loved the calendar date range selector in Google Analytics, it’s extremely user-friendly for the task of selecting a range of dates; much more so than the typical dual input boxes with independent calendars. There are a few options out there for replicating the Google Analytics range picker, however I couldn’t seem to find any that offered the functionality, looks, and ease of use right out of the box. The closest I could find was the DatePicker offered by Stefan Petre, so I decided to use that as a starting point and create a Google Analytics inspired DatePicker of my own.

My version builds off of Petre’s with the following changes:

  • Split stylesheet into base.css to allow easier styling, and dark.css as the first full style.
  • Created clean.css to provide a style similar to Google Analytics.
  • Clicking the calendar month title selects the whole month when in ‘range’ mode.
  • Removed the ‘week’ column.
  • Heavy refactoring/commenting of the code.
  • Created new examples page and documentation suite.

Documentation and Download

See the following links below for the source code, examples page and documentation page.

Widget Code

To create the above date drop-down widget, you can use code like the following:

Includes

First download jQuery and DatePicker and include them:

<script type="text/javascript" src="/js/jquery/jquery.js"></script>
<script type="text/javascript" src="/js/datepicker/js/datepicker.js"></script>
<link rel="stylesheet" type="text/css" href="/js/datepicker/css/base.css" />
<link rel="stylesheet" type="text/css" href="/js/datepicker/css/clean.css" />

HTML Markup

Add the following HTML markup where you want the widget positioned:

<div id="date-range">
  <div id="date-range-field">
    <span></span>
    <a href="#">&#9660;</a>
  </div>
  <div id="datepicker-calendar"></div>
</div>

JavaScript

The following JavaScript will bind the DatePicker calendar to the <div>, update the date display, and handle displaying/hiding the calendar on click.

<script type="text/javascript">
  $(document).ready(function() {

  var to = new Date();
  var from = new Date(to.getTime() - 1000 * 60 * 60 * 24 * 14);

  $('#datepicker-calendar').DatePicker({
    inline: true,
    date: [from, to],
    calendars: 3,
    mode: 'range',
    current: new Date(to.getFullYear(), to.getMonth() - 1, 1),
    onChange: function(dates,el) {
      // update the range display
      $('#date-range-field span').text(dates[0].getDate()+' '+dates[0].getMonthName(true)+', '+dates[0].getFullYear()+' - '+
                                        dates[1].getDate()+' '+dates[1].getMonthName(true)+', '+dates[1].getFullYear());
     }
   });

   // initialize the special date dropdown field
   $('#date-range-field span').text(from.getDate()+' '+from.getMonthName(true)+', '+from.getFullYear()+' - '+
                                        to.getDate()+' '+to.getMonthName(true)+', '+to.getFullYear());

   // bind a click handler to the date display field, which when clicked
   // toggles the date picker calendar, flips the up/down indicator arrow,
   // and keeps the borders looking pretty
   $('#date-range-field').bind('click', function(){
     $('#datepicker-calendar').toggle();
     if($('#date-range-field a').text().charCodeAt(0) == 9660) {
       // switch to up-arrow
       $('#date-range-field a').html('&#9650;');
       $('#date-range-field').css({borderBottomLeftRadius:0, borderBottomRightRadius:0});
       $('#date-range-field a').css({borderBottomRightRadius:0});
     } else {
       // switch to down-arrow
       $('#date-range-field a').html('&#9660;');
       $('#date-range-field').css({borderBottomLeftRadius:5, borderBottomRightRadius:5});
       $('#date-range-field a').css({borderBottomRightRadius:5});
     }
     return false;
   });

   // global click handler to hide the widget calendar when it's open, and
   // some other part of the document is clicked.  Note that this works best
   // defined out here rather than built in to the datepicker core because this
   // particular example is actually an 'inline' datepicker which is displayed
   // by an external event, unlike a non-inline datepicker which is automatically
   // displayed/hidden by clicks within/without the datepicker element and datepicker respectively
   $('html').click(function() {
     if($('#datepicker-calendar').is(":visible")) {
       $('#datepicker-calendar').hide();
       $('#date-range-field a').html('&#9660;');
       $('#date-range-field').css({borderBottomLeftRadius:5, borderBottomRightRadius:5});
       $('#date-range-field a').css({borderBottomRightRadius:5});
     }
   });

   // stop the click propagation when clicking on the calendar element
   // so that we don't close it
   $('#datepicker-calendar').click(function(event){
     event.stopPropagation();
   });
 });
</script>

CSS

And finally the CSS to style the dropdown widget:

<style type="text/css">
  /* Style the calendar custom widget */
  #date-range {
    position:relative;
  }
  #date-range-field {
    width: 290px;
    height: 26px;
    overflow: hidden;
    position: relative;
    cursor:pointer;
    border: 1px solid #CCCCCC;
    border-radius: 5px 5px 5px 5px;
  }

  #date-range-field a  {
    color:#B2B2B2;
    background-color:#F7F7F7;
    text-align:center;
    display: block;
    position: absolute;
    width: 26px;
    height: 23px;
    top: 0;
    right: 0;
    text-decoration: none;
    padding-top:6px;
    border-radius: 0 5px 5px 0;
  }

  #date-range-field span {
    font-size: 12px;
    font-weight: bold;
    color: #404040;
    position: relative;
    top: 0;
    height: 26px;
    line-height: 26px;
    left: 5px;
    width: 250px;
    text-align: center;
  }

  #datepicker-calendar {
    position: absolute;
    top: 27px;
    left: 0;
    overflow: hidden;
    width: 497px;
    height:153px;
    background-color: #F7F7F7;
    border: 1px solid #CCCCCC;
    border-radius: 0 5px 5px 5px;
    display:none;
    padding:10px 0 0 10px;
  }

  /* Remove default border from the custom widget since we're adding our own. */
  #datepicker-calendar div.datepicker {
    background-color: transparent;
    border: none;
    border-radius: 0;
    padding: 0;
  }
</style>

Published by Nik McLaughlin

You can find Nik around the WP space, on LinkedIn, or on his personal blog.

35 Comments

  1. Awesome work, that’s exactly the kind of tool I was looking for. I too find the other date range selector not usable enough, yours is really clean.

    Thanks for sharing this with the Internet.

    • Why you’re very welcome, I’d still like to simplify it some more, but I’m glad you like it!

  2. this looks great! the only thing it seems to be missing from the GA version is the option to compare to past. is this something that can be added easily?

    thanks!

  3. is this compatible for IE 8?

  4. hi justin,

    did you ever look at the compare to date function of like google analytics has?

    thanks

    • Ha, no, never really tried that out until just now. That’s pretty cool. Not sure what it would take to add something like that to my version

  5. 1.How to disable the future date? i.e. The user is no need to select the future date and it will not be a clickable, it will display like a disable state.

    2.How to disable the past date? i.e. If the user is select one past date, then the selected dates before dates are display like a disable state.

    • Hey Raj, there’s no built-in configuration option to do what you’re describing. You should be able to achieve what you’re describing by providing a custom handler for the onRenderCell event. See the Events section of the documentation. By providing an onRenderCell handler, your function will be passed each calendar cell element, along with the displayed date, and you could compare the displayed date to the current date, setting the ‘disabled’ property on the element when needed. Hope this helps

  6. so in that date range picker, if you are to send that form via a cgi script, what would the request look like to CGI? in other words, I copied your code and put it on my page and I can now see a data range picker but I don’t know how to use that in HTMl form. Any example you can provide would be very valuable.

    Thanks

    • Take a look at the documentation page: http://foxrunsoftware.github.com/DatePicker/reference.html You have two basic approaches you can take: you can either grab the current value from the datepicker element when the form is submitted, with something like: $('#datepicker-calendar').DatePickerGetDate(). Or you can grab the date(s) by listening for the onChange event. Either way, you’re going to have to use a bit of javascript to write out for example a hidden field element within your form, and figure out how you want to represent the date, or even date ranges, depending on the type of calendar widget you’re using. I didn’t show any code examples because the specifics depend too much on your particular page markup/date requirements, but it will take some JavaScript know-how to get working.

  7. Nice! Is it possible to select a date range in months (e.g. Feb 2011 to Oct 2012) with on the top year (instead of month and year) and the bottom the months rather than the days? (http://www.eyecon.ro/bootstrap-datepicker)

  8. Thanks Man.

  9. Hey justin, i cant figure out how i disable (not selectable, not clickable and with different color, red probably:)) a specific date with an array. Can you a simple example? thanks!

    • Hey Anna, unfortunately there’s no option to disable dates using an array. You’d have to make use of the onRenderCell event described in the API reference, and disable the cell based on the date; but to do so requires some JavaScript knowledge/know-how

  10. On the GitHub page of the project, there is no detailed HTML plus JavaScript function example for “Calendar With Custom Widget”. I have tried looking at the source code but I can’t get it to work. A detailed example would be awesome for us beginners.

  11. Dear Justin,

    thanks for this! it’s really nice and what I’m one more looking for this like everyone else here 😀

    Do you recommend to use it in production? Or it’s just still experimental? I ask that because it have almost a year that there is no updates… (1.0.0 version is from 3/12/2012)

    And for that, I ask if has no incompatibility with the newest versions of jquery and jquery-ui?

    anyway, thanks for the attention

  12. How to use it for multiple datepicker in a form?

  13. Hi justin,

    Thanks it is nice. can i disable the future date/Month from the opened calender.is there any example of it. Thanks

    Mohan

    • Hi justin,

      It is working now. Can we disable all the dates which is not in our selection.

      How we can we do this suppose i have selected 5 apr to 10 may then rest dates should be disable. Can we do this.

      Thanks

      Ashish

  14. Is there an example when using the non inline events like onBeforeShow, onAfterShow? I can’t see that they’re triggered even if I set inline to false.

  15. Thank you for the great widget,

    I want to ask you if there’s a way to specify two dates in the current variable. for example to populate the range date that you have already in the database.

    It would be really helpful!

    • Hey Amine, if I understand correctly I think you should be able to do this by passing two dates to the date option as described in the documentation. Ie $('#datepicker-calendar').DatePicker({date: [new Date(), new Date() + 345600000});

  16. This is really good framework. How to give facility to user to jump the year? Client want to have drop-down to change year /months or double arrows to jump year. Please suggest.

  17. May I know where to edit if i need to add no week to be displayed?

  18. I wonder Mr. Justin if it is possible to limit the number of days selected in the range of two dates.

    For example; i want the user to select a maximum number of days that is less or equals 30 days between two dates.

    Is there any workaround for this, i loved the widget and i wanted to integrate it in my website but this functional requirement is highly important to me and my endeavors.

Hmm, looks like this article is quite old! Its content may be outdated, so comments are now closed.