CLNDR.js
CLNDR is a jQuery calendar plugin. It was created -- you've heard this before -- out of frustration with the lack of truly dynamic front-end calendar plugins out there.
See a demo: kylestetz.github.io/CLNDR/
Download
- Development: clndr.js
- Production: clndr.min.js
Returning to grab a new version? Have a look at the CHANGELOG.md
file.
If you'd like to run some tests in a particular browser or environment,
tests/test.html
contains a list of basic functionality tests. When
contributing, please run these (and add to them when appropriate) before
submitting a pull request or issue!
Dependencies
jQuery and Moment.js are
depended upon. By default CLNDR tries to use
Underscore.js's _.template()
function, however if
you specify a custom rendering function (see documentation below) Underscore
will not be used at all.
Because their APIs are the same, Lo-Dash's _.template()
function will work as well! Just include Lo-Dash instead of Underscore.
Using NPM
You can install CLNDR via NPM:
npm install clndr
Underscore is not installed by default. This allows you to use whichever
templating engine you want to. If you want to use the default template
option
with Underscore, just install it as a dependency of your project:
npm install underscore
or npm install lodash
.
Using Bower
You can install CLNDR via Bower:
bower install clndr
Underscore is not installed by default. This allows you to use whichever
templating engine you want to. If you want to use the default template
option
with Underscore, just install it as a dependency of your project:
bower install underscore
or bower install lodash
.
CLNDR Using Angular
If you want to integrate clndr into an angular.js site, get started with this directive: angular-clndr.
CLNDR Using Rails
If you're building a rails application you may be interested in this gem by @sedx: clndr-rails.
Introduction: You Write The Markup
There are wonderful and feature-rich calendar modules out there and they all suffer the same problem: they give you markup (and often a good heap of JS) that you have to work with and style. This leads to a lot of hacking, pushing, pulling, and annoying why-can't-it-do-what-I-want scenarios.
CLNDR doesn't generate markup (well, it has some reasonable defaults, but that's an aside). Instead, CLNDR asks you to create a template and in return it supplies your template with a great set of objects that will get you up and running in a few lines.
The 'Days' Array
Here's a typical CLNDR template. It's got a controller section and a grid section.
‹ <%= month %> › <% _.each(daysOfTheWeek, function (day) { %> <%= day %> <% }) %> <% _.each(days, function (day) { %> <%= day.day %> <% }) %>
The days
array contains most of the stuff we need to make a calendar. Its
structure looks like this:
day: 5 events: classes: "day" date:
This makes quick work of generating a grid. days.classes
contains extra
classes depending on the circumstance: if a given day is today, 'today' will
show up, as well as an 'event' class when an event lands on that day.
Pass In Your Events
CLNDR accepts events as an array of objects:
events = date: "YYYY-MM-DD or some other ISO Date format" and: "anything else"
CLNDR looks through the objects in your events array for a date
field unless
you specify otherwise using the dateParameter
option. In your template the
days
array will auto-magically contain these event objects in their entirety.
See the examples for a demonstration of how events populate the days
array.
Usage
CLNDR leans on the awesome work done in Underscore and moment. These are
requirements unless you are using a different rendering engine, in which case
Underscore is not a requirement). Do be sure to include them in your <head>
before clndr.js. It is a jQuery plugin, so naturally you'll need that as well.
The bare minimum (CLNDR includes a default template):
;
With all of the available options:
;
All of the things you have access to in your template:
// An array of day-of-the-week abbreviations, shifted as requested using the// weekOffset parameter.daysOfTheWeek: 'S' 'M' 'T' etc... // The number of 7-block calendar rows, in the event that you want to do some// looping with itnumberOfRows: 5 // The days array, documented in more detail abovedays: day classes id events date // The month name- don't forget that you can do things like// month.substring(0, 1) and month.toLowerCase() in your templatemonth: "May" // The year that the calendar is currently focused onyear: "2013" // All of the events happening this month. This will be empty of the// lengthOfTime config option is set.eventsThisMonth: // All of the events happening last month. This is only set if// showAdjacementMonths is true.eventsLastMonth: // All of the events happening next month. This is only set if// showAdjacementMonths is true.eventsNextMonth: // If you specified a custom lengthOfTime, you will have these instead.intervalEnd: moment objectintervalStart: moment objecteventsThisInterval: // Anything you passed into the 'extras' property when creating the clndrextras: {}
Multi-day Events
CLNDR accepts events lasting more than one day. You just need to tell it how to access the start and end dates of your events:
var lotsOfEvents = end: '2013-11-08' start: '2013-11-04' title: 'Monday to Friday Event' end: '2013-11-20' start: '2013-11-15' title: 'Another Long Event' ; ;
When looping through days in my template, 'Monday to Friday Event' will be passed to every single day between the start and end date. See index.html in the example folder for a demo of this feature.
Mixing Multi- and Single-day Events
If you also have single-day events mixed in with different date fields, as of
clndr v1.2.7
you can specify a third property of multiDayEvents
called
singleDay
that refers to the date field for a single-day event.
var lotsOfMixedEvents = [
{
end: '2015-11-08',
start: '2015-11-04',
title: 'Monday to Friday Event'
}, {
end: '2015-11-20',
start: '2015-11-15',
title: 'Another Long Event'
}, {
title: 'Birthday',
date: '2015-07-16'
}
];
$('#calendar').clndr({
events: lotsOfEvents,
multiDayEvents: {
endDate: 'end',
singleDay: 'date',
startDate: 'start'
}
});
Custom Classes
The classes that get added to a day
object automatically can be customized to
avoid styling conflicts. The classes
option accepts today
, event
, past
,
lastMonth
, nextMonth
, adjacentMonth
, and inactive
. Pass in only the
classnames you wish to override and the rest will be set to their defaults.
In this example we create a my-
namespace for all of the classes:
clndrcustomClasses = ;
To configure the day
, empty
, and next/previous/today/etc. button classes,
use the targets
option documented in the
usage section.
Constraints & Datepickers
If you are making a datepicker or you'd just like to prevent users from
next
ing all the way to 2034 in your calendar, you can pass a constraints
option with startDate
, endDate
, or both specified:
;
Now your calendar's next and previous buttons will only work within this date range. When they become disabled they will have the class 'inactive', which you can use to gray them out or add gif flames or whatever.
The days in your grid that are outside of the range will also have the
inactive
class. This means that you will want to add a click callback and
check for whether or not a day has the class inactive
. It will look like this:
;
The constraints can be updated at any time via clndr.options.constraints
. If
you make a change, call render()
afterwards so that clndr can update your
interface with the appropriate classes.
myCalendaroptionsconstraintsstartDate = '1999-12-31';myCalendar;
Make sure the startDate
comes before the endDate
!
Returning the Instance / Public API
It's possible to save the clndr object in order to call it from JS later. There are functions to increment or set the month or year. You can also provide a new events array.
// Create a CLNDR and save the instance as myCalendarvar myCalendar = ; // Go to the next monthmyCalendar; // Go to the previous monthmyCalendar; // Set the month using a number from 0-11 or a month namemyCalendar;myCalendar; // Go to the next yearmyCalendar; // Go to the previous yearmyCalendar; // Set the yearmyCalendar; // Go to today:myCalendar; // Overwrite the extras. Note that this triggers a re-render of the calendar.myCalendar; // Change the events. Note that this triggers a re-render of the calendar.myCalendar; // Add events. Note that this triggers a re-render of the calendar.myCalendar; // Remove events. All events for which the passed in function returns true will// be removed from the calendar. Note that this triggers a re-render of the// calendar.myCalendar; // Destroy the clndr instance. This will empty the DOM node containing the// calendar.myCalendar;
If you are taking advantage of the onMonthChange
and onYearChange
callbacks,
you might want them to fire whenver you call setMonth
, setYear
, forward
,
back
, etc. Just pass in an object as an argument with withCallbacks: true
like this:
// Month will be set to February and then onMonthChange will be fired.myCalendar; // Month will increment and onMonthChange, and possibly onYearChange, will be// fired.myCalendarnext withCallbacks: true ;
Template Requirements
CLNDR is structured so that you don't really need anything in your template.
<% _; %>
Currently CLNDR sets the class on a day to 'calendar-day-2013-05-30'
and uses
it to determine the date when a user clicks on it. Thus, click events will only
work if days.classes
is included in your day element's class
attribute as
seen above.
Configuration
Template Rendering Engine
You can pass in a render
function as an option, for example:
var precompiledTemplate = myRenderingEngine; ;
Where the function must return the HTML result of the rendering operation. In this case you would precompile your template elsewhere in your code, since CLNDR only cares about your template if it's going to use Underscore.
If you are using your own render method, Underscore is NOT a dependency of this plugin.
CLNDR has been tested successfully with doT.js, Hogan.js, Handlebars.js, Mustache.js, and Knockout.js. Please get in touch if you have success with other languages and they will be documented here.
Here's an example using doT.js...
The markup:
The Javascript:
var clndrTemplate = doT; ;
Here's an example using Mustache.js...
The markup:
The Javascript:
;
Internationalization
CLNDR has support for internationalization insofar as Moment.js supports it. By configuring your Moment.js instance to a different language, which you can read more about here: i18n in Moment.js, you are configuring CLNDR as well.
If you would prefer to pass in a pre-configured instance of moment, you can do
this by passing it in as the moment
config option when initializing CLNDR:
// To change clndr to German use moment.local('de')moment; // Make sure that your locale is Working correctlyconsole// Returns "heute um 18:43 Uhr" ;
If you are using a moment.js language configuration in which weeks begin on a
Monday (e.g. French), CLNDR will detect this automatically and there is no need
to provide a weekOffset
or a daysOfTheWeek
array. If you want to reverse
this behavior, there is a field in each moment.js language config file called
dow
which you can set to your liking.
The day of the week abbreviations are created automatically using moment.js's
current language setting, however if this does not suit your needs you should
override them using the daysOfTheWeek
option. Make sure the array you provide
begins on the same day of the week as your current language setting.
Warning: using daysOfTheWeek
and weekOffset
in conjunction with
different language settings is not recommended and may cause you headaches.
Underscore Template Delimiters
If you're not a fan of <% %>
and <%= %>
style delimiters you can provide
Underscore.js with alternatives in the form of regular expressions. There are
three delimiters...
interpolate, which outputs a string (this is <%= %>
by default)
escape, for escaping HTML (this is <%- %>
by default)
evaluate, for evaluating javascript (this is <% %>
by default)
If you're more comfortable with Jinja2/Twig/Nunjucks style delimiters, simply call this before you instantiate your clndr:
// Switch to Jinja2/Twig/Nunjucks-style delimiters_templateSettings = escape: /\{\{\-\}\}/g evaluate: /\{\%\%\}/g interpolate: /\{\{\}\}/g;
Internet Explorer Issues
If you're planning on supporting IE8 and below, you'll have to be careful about
version dependencies. You'll need the jQuery 1.10.x branch for IE support, and
if you're taking advantage of the constraints
feature you'll need to use a
version of moment.js <=2.1.0
or >=2.5.1
.
Submitting Issues
GitHub issues and support tickets are to be submitted only for bugs. We sadly don't have the time or manpower to answer implementation questions, debug your application code, or anything that isn't directly related to a CLNDR bug :D There are many wonderful places to seek help, like Stack Overflow.