Skip to main content

Selecting date range with YUI3 Calendar

This is a fully UI related article about YUI3 Calendar widget. Whereas official documentation page and provided examples (here and here) are good enough to start with, there are still many pitfalls when you're going to customize the widget. My use case required a date range selection feature with a nice calendar window open only when a user clicks on text input fields. Also the selected day should be highlighted in the calendar whenever a user clicks again on the filled text input field (a nice example with YUI2 Calendar widget). This is how one date input field looks like:

Add calendar container and date fields on the web page
First of all you need to add a container div for the calendar:
<div id="calendar-container"> </div>
You will provide this id to the Calendar constructor later either as a bounding box or a content box. The difference is in html markup generated by YUI3. I'll use a bounding box below in order to manipulate position of calendar window.

Also you will need input text fields on your page to be filled with dates:
<input id="startDate" type="text"/>
<input id="endDate" type="text"/>

Initialize YUI3
To get started with YUI3, you should perform two steps:
  1. Load the YUI javascript file on your web page.
  2. Create and configure a new YUI instance.
Please refer to this manual for code snippets. The only difference is that we'll use two modules: calendar and datatype-date:
YUI().use('calendar', 'datatype-date',  function(yui3) {
// Calendar code will be here
}

Initialize Calendar
Here we'll define some variables to be used in functions later and the calendar instance itself.
// Our calendar bounding div id
var boundingBoxId = "#calendar-container",
// This flag used to track mouse position
    isMouseOverCalendar = false,
// A text field element that stores the date chosen in calendar
    currentValueContainer = '',
    calendar = new yui3.Calendar({
        boundingBox: boundingBoxId,
        width: "300px"
    });

Register event handler functions
Here we'll define event handlers for several DOM events (focus, blur, mouseover, mouseout) and for a selectionChange event of our calendar:
// These are text fields' ids to store dates in
var dateFields = ["#startDate", "#endDate"];

// To show calendar when user clicks on text fields
yui3.on("focus", function(event) { showCalendar(event) }, dateFields);
// To hide calendar when text fields loose focus
yui3.on("blur", function() { hideCalendar() }, dateFields);

// Tracking mouse position
yui3.on("mouseover", function () {
    isMouseOverCalendar = true;
}, boundingBoxId);
yui3.on("mouseout", function () {
    isMouseOverCalendar = false;
}, boundingBoxId);

// On date selection change we update value of a text field and hide calendar window
calendar.on("selectionChange", function (event) {
    var newDate = event.newSelection[0];
    yui3.one(currentValueContainer).set("value", yui3.DataType.Date.format(newDate));
    isMouseOverCalendar = false;
    hideCalendar();
});

Show Calendar function
This is the most complex part of functionality to show the calendar properly. See inline comments for the details:
var showCalendar = function (event) {
    // It's a text field that a user clicked on
    currentValueContainer = event.target;

    // Saving position of a text field to calculate position of calendar
    var xy = yui3.one(currentValueContainer).getXY(), 
    // Getting current date value in the text field
        dateString = yui3.one(currentValueContainer).get("value");

    // Clearing previously selected dates if any
    calendar.deselectDates();
    // If the text field had some date value before
    if (dateString) {
        // Parsing the date string into JS Date value
        var date = yui3.DataType.Date.parse(dateString);
        if (date) {
            // Highlighting the date stored in the text field
            calendar.selectDates(date);
        } else {
            date = new Date();
        }
        // Setting calendar date to show corresponding month
        calendar.set("date", date);
    } else {
        calendar.set("date", new Date());
    }
    // Finally render the calendar window
    calendar.render();

    // Required styles to show calendar in a proper position
    yui3.one(boundingBoxId).setStyles({
        display: "block",
        position: "absolute"
    });
    // Set calendar window position right below the text field
    yui3.one(boundingBoxId).setXY([xy[0], xy[1] + 20]);
};
I should give some remarks here:
  • This doesn't fully work in IE - you will loose the currently selected date when you click on the text field again. That's because IE cannot parse dateString into valid JS Date but returns null instead.
  • The single instance of Calendar is used on the page. That's why we have to deselect old date, parse date value stored in the text field and select this date in calendar manually. However, you benefit from it when you have many date selection text fields on the page (in my case there are 3 date ranges, thus, 6 date text fields).
  • Check YUI3 Calendar API page for more details.

Hide Calendar function
This function simply hides the calendar window from the page.
var hideCalendar = function () {
    if (!isMouseOverCalendar) {
        yui3.one(boundingBoxId).setStyle("display", "none");
    }
};
That's it. It works flawlessly in Chrome and Firefox, also quite nice in IE8 except loosing selected date when clicking text fields again. YUI version used is 3.4.1.

Comments

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Hi..same code iam using in yui3 calendar development..its not working..please help me to find.
    My code

    YUI().use('calendar', 'datatype-date', function(Y) {
    var boundingBoxId = "#calendar-container",
    isMouseOverCalendar = false,
    currentValueContainer = '',
    calendar = new Y.Calendar({
    boundingBox: boundingBoxId,
    width: "300px"
    });

    var dateFields = ["#startDate", "#endDate"];

    Y.on("focus", showCalendar, dateFields);
    Y.on("blur", hideCalendar, dateFields);

    Y.on("mouseover", function () {
    isMouseOverCalendar = true;
    }, boundingBoxId);

    Y.on("mouseout", function () {
    isMouseOverCalendar = false;
    }, boundingBoxId);

    calendar.on("selectionChange", function (event) {
    var newDate = event.newSelection[0];
    Y.one(currentValueContainer).set("value", Y.DataType.Date.format(newDate));
    isMouseOverCalendar = false;
    hideCalendar();
    });

    var showCalendar = function (event) {
    // It's a text field that a user clicked on
    currentValueContainer = event.target;

    // Saving position of a text field to calculate position of calendar
    var xy = Y.one(currentValueContainer).getXY(),
    // Getting current date value in the text field
    dateString = Y.one(currentValueContainer).get("value");

    // Clearing previously selected dates if any
    calendar.deselectDates();
    // If the text field had some date value before
    if (dateString) {
    // Parsing the date string into JS Date value
    var date = Y.DataType.Date.parse(dateString);
    if (date) {
    // Highlighting the date stored in the text field
    calendar.selectDates(date);
    } else {
    date = new Date();
    }
    // Setting calendar date to show corresponding month
    calendar.set("date", date);
    } else {
    calendar.set("date", new Date());
    }
    // Finally render the calendar window
    calendar.render();

    // Required styles to show calendar in a proper position
    Y.one(boundingBoxId).setStyles({
    display: "block",
    position: "absolute"
    });
    // Set calendar window position right below the text field
    Y.one(boundingBoxId).setXY([xy[0], xy[1] + 20]);
    };

    var hideCalendar = function () {
    if (!isMouseOverCalendar) {
    Y.one(boundingBoxId).setStyle("display", "none");
    }
    };

    });


    thanks.

    ReplyDelete
    Replies
    1. Hi, indeed it didn't work properly until I modified the following code snippet in the article:

      // To show calendar when user clicks on text fields
      yui3.on("focus", function(event) { showCalendar(event) }, dateFields);
      // To hide calendar when text fields loose focus
      yui3.on("blur", function() { hideCalendar() }, dateFields);

      Please check if it works for you.

      Delete

Post a Comment

Popular posts from this blog

DynamicReports and Spring MVC integration

This is a tutorial on how to exploit DynamicReports reporting library in an existing Spring MVC based web application. It's a continuation to the previous post where DynamicReports has been chosen as the most appropriate solution to implement an export feature in a web application (for my specific use case). The complete code won't be provided here but only the essential code snippets together with usage remarks. Also I've widely used this tutorial that describes a similar problem for an alternative reporting library.
So let's turn to the implementation description and start with a short plan of this how-to:
Adding project dependencies.Implementing the Controller part of the MVC pattern.Modifying the View part of the MVC pattern.Modifying web.xml.Adding project dependencies
I used to apply Maven Project Builder throughout my Java applications, thus the dependencies will be provided in the Maven format.

Maven project pom.xml file:
net.sourceforge.dynamicreportsdynamicrepo…

Choosing Java reporting tool - part 2

I've provided a general overview of possible solutions to get a reporting/exporting functionality in the previous post. This is the second overview of alternatives based on JasperReports reporting engine.

Since the previous part I've done the following:
Implemented a simple report using both DynamicJasper and DynamicReports to compare them from technical side.Investigated JasperServer features and tried to implement a simple report for JasperServer instance (it appeared we already have a ready licensed installation of JasperServer that makes it unreasonable to install a fresh one).
First, the comparison results of Java libraries (DynamicJasper and DynamicReports):
Both libraries suffer from poor-quality or missing Java docs but they look a bit better in DynamicJasper.Taking into account the point 1, a developer has to use online documentation and to review the code. Here the code looks definitely nicer and more readable for DynamicReports. With respect t…

Do It Yourself Java Profiling

This article is a free translation of the Russian one that is a transcript of the Russian video lecture done by Roman Elizarov at the Application Developer Days 2011 conference.
The lecturer talked about profiling of Java applications without any standalone tools. Instead, it's suggested to use internal JVM features (i.e. threaddumps, java agents, bytecode manipulation) to implement profiling quickly and efficiently. Moreover, it can be applied on Production environments with minimal overhead. This concept is called DIY or "Do It Yourself". Below the lecture's text and slides begin.
Today I'm giving a lecture "Do It Yourself Java Profiling". It's based on the real life experience that was gained during more than 10 years of developing high-loaded finance applications that work with huge amounts of data, millions currency rate changes per second and thousands of online users. As a result, we have to deal with profiling. Application profiling is an i…