x-calendar

« Back to docs

Provides a simple calendar UI widget.

Note that when the API works with datestrings, while it's best to pass in "YYYY-MM-DD" ISO strings to the calendar, the <x-calendar> will also accept any string parseable by Date.parse(). However, be aware that browser implementations of Date.parse() may differ slightly.

Basic usage

Markup

<x-calendar></x-calendar>

Basic calendar with navigation controls

To add a simple set of navigation controls to the calendar, simply add the controls attribute to the <x-calendar>

Markup

<x-calendar controls></x-calendar>

Initializing the viewport with view

While calendars default to focusing on the current day, by setting a date in the view attribute of the <x-calendar>, users can specify the initial viewport of the calendar.

Markup

<x-calendar view="2013-06-09" controls></x-calendar>

Initializing selected dates with chosen/.chosen

While the user can directly select a date on the calendar, the calendar can also be initialized with a starting chosen date.

The chosen attribute is also accessible with the the .chosen property.

Markup

<x-calendar chosen="2012-05-17" controls></x-calendar>

Equivalent initial JavaScript

// assume calendar is already defined to be the
// <x-calendar> DOM node
calendar.chosen = "2013-05-17";
// ...or...
calendar.chosen = "May 17, 2013";
// ...or...
// remember that Date() months start at 0, not 1
calendar.chosen = new Date(2013, 4, 17);

Allowing multi-selection with multiple

While the calendar defaults to only allowing one chosen date at a time, when the multiple attribute is applied, multiple dates can be chosen at once. (Try dragging the cursor around to paint over dates.)

In addition, this allows the chosen attribute to take more than one date. To do so, pass in a JSON string for an array whose elements are either: strings signifying single dates, or two-element arrays of strings signifying the start and end of a date range.

Important note: Because these must be JSON strings, make sure that all quoted strings in the attribute string are using double quotes, not single quotes!

If assigning to the calendar's .chosen property instead, the user can provide the actual array instead of a JSON string, and Date objects may be used instead of strings.

The multiple attribute is also accessible with the the .multiple property.

Markup

<x-calendar multiple controls
chosen='[["2013-06-04", "2013-06-10"], "2013-06-19", ["2013-06-29","2013-07-02"]]'>
</x-calendar>

Equivalent initial JavaScript

// assume calendar is already defined to be the
// <x-calendar> DOM node
calendar.chosen = '[["2013-06-04", "2013-06-10"], "2013-06-19", ["2013-06-29","2013-07-02"]]';
// ...or...
calendar.chosen = [["2013-06-04", "2013-06-10"], "2013-06-19", ["2013-06-29","2013-07-02"]];
// ...or...
// remember that Date() months start at 0, not 1
calendar.chosen = [[new Date(2013, 5, 4), new Date(2013, 5, 10)],
new Date(2013, 5, 19),
[new Date(2013, 5, 29), new Date(2013, 6, 2)]
];

Preventing date selection with notoggle

By default, the <calendar> allows the user to select dates by clicking individual days.

However, if the notoggle attribute is provided, the calendar disables choosing dates through the interface. (This does not prevent developers from programmatically changing the chosen dates.)

This prevents datetoggleon and datetoggleoff events from being triggered normally through the interface, but datetap events are unaffected. (See events demo for details on these events)

Markup

<x-calendar notoggle controls></x-calendar>

Displaying multiple months with span

By default, the calendar only shows one month at a time, but by providing a number to the span attribute, the calendar can display as many months as the user needs.

The span attribute is also accessible with the the .span property.

Markup

<x-calendar controls span=2></x-calendar>

Changing the starting weekday with first-weekday-num/.firstWeekdayNum

By default, the calendar uses Sunday as the first day of the week. However, in many regions, this may not be the case, so the <x-calendar> API provides the first-weekday-num attribute to change which day starts the week.

The number passed to first-weekday-num should be a number between 0 to 6, where 0=Sunday, 1=Monday, etc.

The first-weekday-num attribute is also accessible with the the .firstWeekdayNum property.

Markup

<x-calendar controls first-weekday-num=1></x-calendar>

Changing the calendar's labels with .labels

While the calendar defaults to using English labels, these labels can be edited by the user by editing the .labels property of the calendar.

When changing labels, the given data should be a JS object that can contain any of the following key:value pairs:

{
    "prev": a string to display on the previous-month navigation button,
    "next": a string to display on the next-month navigation button,
    "months": an array of 12 strings, where the first string corresponds to January, the second to February, etc, all the way up to December.,
    "weekdays": an array of 7 strings, where the first string corresponds to Sunday, the second to Monday, etc, all the way up to Saturday.
}

If the new data given does not have any of these keys, that corresponding particular label will remain unchanged.

For example, we can use this to provide a French-language calendar.

Markup

<x-calendar controls first-weekday-num=1></x-calendar>

JavaScript

var frenchCal = document.querySelector("x-calendar[lang=fr]");
frenchCal.labels = {
prev: "<<",
next: ">>",
months: ["janvier", "f\u00E9vrier", "mars", "avril", "mai",
"juin", "juillet", "ao\u00FBt", "septembre", "octobre",
"novembre", "d\u00E9cembre"],
weekdays: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"]
};

Events demo

To provide API hooks for developers, the <x-calendar> fires several different events as it is interacted with:

  • datetoggleon events are fired when a date is first chosen/turned on. This event also provides the following information in e.detail:
    {
            'date': the Date object corresponding to the toggled date,
            'iso': the ISO-formatted string representing the toggled date
        }
  • datetoggleoff events are similar to datetoggleoff, but are fired when a date is unchosen/turned off. This event also provides the following information in e.detail:
    {
            'date': the Date object corresponding to the toggled date,
            'iso': the ISO-formatted string representing the toggled date
        }
  • datetap events are fired when the user taps a day without dragging/painting over other dates. The event also receives the following custom datamap in e.detail:
    {
            'date': the Date object corresponding to the tapped date,
            'iso': the ISO-formatted string representing the tapped date
        }
  • prevmonth and nextmonth events are fired whenever the user uses the calendar's navigation controls to move back or forward a month, respectively.

Markup

<x-calendar controls multiple></x-calendar>

Events


        

Styling the calendar

Due to the number of elements in the calendar, utilize any of the following CSS selectors to customize the calendar's appearance:

  • To style the calendar's container, apply styles to x-calendar.
  • To style the month label, apply styles to x-calendar .month-label.
  • To style individual months, apply styles to x-calendar .month.
    • Note that to change how wide months are, apply the style here, as the width of days and weeks are percentages in relation to this width
    • Also note that a month's max-width is constrained by the width of the x-calendar.
  • To style individual weeks, apply styles to x-calendar .week.
  • To style individual days, apply styles to x-calendar .day.
  • To style the row of weekday labels, apply styles to x-calendar .weekday-labels.
  • To style individual weekday labels, apply styles to x-calendar .weekday-label.
  • To style the current day, apply styles to x-calendar .day.today.
  • To style days that are not in the current month, apply styles to x-calendar .day.badmonth
  • To style chosen days, apply styles to x-calendar .day.chosen
  • To style the previous and next navigation control buttons, apply styles to x-calendar .prev and x-calendar .next.
  • To style how elements appear when the calendar is being dragged on, use the x-calendar[active] selector.
    • Similarly, to style the day that is being hovered over during a drag, use x-calendar[active] .day[active]

Markup

<x-calendar id="custom-style-cal" controls></x-calendar>

CSS styling

#custom-style-cal{
background-color: lightsteelblue;
}

#custom-style-cal .month-label{
background-color: rgba(255,255,255,.3);
}

/* use height and line-height to
* modify the height of weeks */
#custom-style-cal .week{
height: 1.9em;
line-height: 1.9em;
}

#custom-style-cal .day{
font-size: .85em;
border-radius: 50%;
border: 1px solid #fff;
box-shadow: inset 1px 1px 4px grey;
}

/* use :not selectors to let default colors
* fall through in those cases */
#custom-style-cal .day:not(:hover):not(.chosen){
background-color: #fff;
}

#custom-style-cal .day.today{
border-color: limegreen;
}

Doing additional rendering with .customRenderFn

In some cases, a developer may want additional ability to style the calendar dynamically beyond the default behavior. To help faciltate this, the x-calendar can take an additional callback function through the .customRenderFn property to call on each displayed day whenever the calendar is rerendered.

The callback function will be called with three parameters: the day's DOM element, the JavaScript Date object corresponding to the day, and the ISO-formatted string version of the date.

Important note: Because this callback function is intended to be run whenever the calendar rerenders, the function should not do anything to cause a rerender of the calendar. In other words, avoid modifying the calendar's attributes directly.

As a simple example, this can be used to selectively apply styles based on date information.

Markup

<x-calendar id="custom-render-simple" controls></x-calendar>

Styling

*[dance-time]{
background-image: url("../../site/img/grounds_keeping_it_real_s1.gif");
color: white;
text-shadow: 1px 1px 4px grey;
}

JavaScript

var cal = document.getElementById("custom-render-simple");
cal.customRenderFn = function(dayEl, date, iso){
// add selector to every 5th day in a month
if(date.getDate() % 5 === 0){
dayEl.setAttribute("dance-time", true);
}
else{
dayEl.removeAttribute("dance-time");
}
};

Putting it all together: creating a simple event scheduler/manager

This is a simple example of how to use the <x-calendar> component's API to create a simple widget that can store and retrieve day-specific event data.

Selected Date: None

Markup

<figure id="scheduler-demo">
<x-calendar controls notoggle></x-calendar>
<span>Selected Date: <span id="scheduler-date">None</span></span>
<textarea id="scheduler-info"></textarea>
<button id="scheduler-save" disabled>Update info</button>
</figure>

CSS styling

#scheduler-demo > *:not(x-calendar){
margin: 0 auto;
display: block;
}

/* give the current day a sunken look */
#scheduler-demo > x-calendar .scheduler-current{
box-shadow: inset 1px 1px 4px grey;
background-color: rgba(65,105,225,0.5);
}

/* days with data have a border */
#scheduler-demo > x-calendar [scheduler-has-info]{
border-color: royalblue;
border-style: dashed;
}

/* days with data also have an icon */
#scheduler-demo > x-calendar [scheduler-has-info]:after{
content: "";
position: absolute;
background-color: royalblue;
right: 0%;
bottom: 0%;
width: 25%;
height: 25%;
border-radius: 50%;
-webkit-transform: translate(50%, 50%);
-moz-transform: translate(50%, 50%);
-o-transform: translate(50%, 50%);
transform: translate(50%, 50%);
}

JavaScript

/** set up custom event handling demo using provided API hooks **/

// the dictionary of iso datestring keys mapped to event data
var DATE_INFO = {};
// the currently displayed date
var CURR_ISO = null;

// aliases for DOM manipulation
var eventsStage = document.getElementById("scheduler-demo");
var eventsCal = eventsStage.querySelector("x-calendar");
var eventsDateHeader = document.getElementById("scheduler-date");
var eventsInfo = document.getElementById("scheduler-info");
var eventsSaveButton = document.getElementById("scheduler-save");

// define a .customRenderFn that provides extra styling
// information for day elements with stored data
eventsCal.customRenderFn = function(dayEl, date, isoStr){
if(isoStr === CURR_ISO){
xtag.addClass(dayEl, "scheduler-current");
}
else{
xtag.removeClass(dayEl, "scheduler-current");
}

if(isoStr in DATE_INFO){
dayEl.setAttribute("scheduler-has-info", true);
}
else{
dayEl.removeAttribute("scheduler-has-info");
}
}

// respond to calendar taps
eventsCal.addEventListener("datetap", function(e){
// grab date info from event as both a Date and a string
var date = e.detail.date;
var dateStr = e.detail.iso;
// get dictionary content for this date
var content = (dateStr && dateStr in DATE_INFO) ?
DATE_INFO[dateStr] : "";
// set up text area
eventsInfo.value = content;
eventsInfo.disabled = !dateStr;
eventsSaveButton.disabled = !dateStr;
// remember currently shown date
CURR_ISO = dateStr;
eventsDateHeader.textContent = (dateStr) ? dateStr : "None";
// programmatically toggle date object with .toggleDateOn
eventsCal.toggleDateOn(date);
// forces rerender of calendar
eventsCal.render();
});

// save button listener; simply adds textarea value to data
eventsSaveButton.addEventListener("click", function(e){
if(CURR_ISO){
if(eventsInfo.value){
DATE_INFO[CURR_ISO] = eventsInfo.value;
}
else{
delete DATE_INFO[CURR_ISO];
}
}
eventsCal.render();
});