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:
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:
- Load the YUI javascript file on your web page.
- Create and configure a new YUI instance.
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.
This comment has been removed by a blog administrator.
ReplyDeleteHi..same code iam using in yui3 calendar development..its not working..please help me to find.
ReplyDeleteMy 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.
Hi, indeed it didn't work properly until I modified the following code snippet in the article:
Delete// 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.