Welcome to a tutorial on how to create a simple pure Javascript Calendar. Are you looking to develop a calendar web app, without using any server-side scripts? Be it for a commercial project, school project, or just curiosity – You have come to the correct place. This guide will walk you through how to create a pure Javascript calendar, all made possible with local storage. Read on!
ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
TABLE OF CONTENTS
DOWNLOAD & DEMO
Firstly, here is the download link to the example code as promised.
QUICK NOTES
- If you want to set the calendar to start the week on Monday instead, set
cal.sMon = true
incalendar.js
.
EXAMPLE CODE DOWNLOAD
Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
JAVASCRIPT CALENDAR DEMO
HOW THE CALENDAR WORKS
All right, let us now get into a little more details on how the calendar works. Not going to explain line-by-line, but here’s a quick walk-through.
PART 1) CALENDAR HTML
<div id="cal-wrap">
<!-- (A) PERIOD SELECTOR -->
<div id="cal-date">
<select id="cal-mth"></select>
<select id="cal-yr"></select>
</div>
<!-- (B) CALENDAR -->
<div id="cal-container"></div>
<!-- (C) EVENT FORM -->
<form id="cal-event">
<h1 id="evt-head"></h1>
<div id="evt-date"></div>
<textarea id="evt-details" required></textarea>
<input id="evt-close" type="button" value="Close"/>
<input id="evt-del" type="button" value="Delete"/>
<input id="evt-save" type="submit" value="Save"/>
</form>
</div>
The HTML should be straightforward enough, there are only 3 sections here:
<div id="cal-date">
The month and year selector.<div id="cal-container">
Where we will display the calendar for the selected month and year.<form id="cal-event">
A form to add/edit the calendar events.
PART 2) CALENDAR INITIALIZE
var cal = {
// (A) PROPERTIES
// (A1) COMMON CALENDAR
sMon : false, // Week start on Monday?
mName : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // Month Names
// (A2) CALENDAR DATA
data : null, // Events for the selected period
sDay : 0, sMth : 0, sYear : 0, // Current selected day, month, year
// (A3) COMMON HTML ELEMENTS
hMth : null, hYear : null, // month/year selector
hForm : null, hfHead : null, hfDate : null, hfTxt : null, hfDel : null, // event form
// (B) INIT CALENDAR
init : () => {
// (B1) GET + SET COMMON HTML ELEMENTS
cal.hMth = document.getElementById("cal-mth");
cal.hYear = document.getElementById("cal-yr");
cal.hForm = document.getElementById("cal-event");
cal.hfHead = document.getElementById("evt-head");
cal.hfDate = document.getElementById("evt-date");
cal.hfTxt = document.getElementById("evt-details");
cal.hfDel = document.getElementById("evt-del");
document.getElementById("evt-close").onclick = cal.close;
cal.hfDel.onclick = cal.del;
cal.hForm.onsubmit = cal.save;
// (B2) DATE NOW
let now = new Date(),
nowMth = now.getMonth(),
nowYear = parseInt(now.getFullYear());
// (B3) APPEND MONTHS SELECTOR
for (let i=0; i<12; i++) {
let opt = document.createElement("option");
opt.value = i;
opt.innerHTML = cal.mName[i];
if (i==nowMth) { opt.selected = true; }
cal.hMth.appendChild(opt);
}
cal.hMth.onchange = cal.list;
// (B4) APPEND YEARS SELECTOR
// Set to 10 years range. Change this as you like.
for (let i=nowYear-10; i<=nowYear+10; i++) {
let opt = document.createElement("option");
opt.value = i;
opt.innerHTML = i;
if (i==nowYear) { opt.selected = true; }
cal.hYear.appendChild(opt);
}
cal.hYear.onchange = cal.list;
// (B5) START - DRAW CALENDAR
cal.list();
};
window.addEventListener("load", cal.init);
cal.init()
is the first thing that runs on page load to initialize the calendar. It looks complicated at first, but keep calm and look carefully – All it does is to set up the HTML interface:
- Add months to the selector.
- Add years to the selector.
- Attach the add/edit event form click and submit handlers.
That’s all.
PART 3) DRAW CALENDAR
// (C) DRAW CALENDAR FOR SELECTED MONTH
list : () => {
// (C1) BASIC CALCULATIONS - DAYS IN MONTH, START + END DAY
// Note - Jan is 0 & Dec is 11
// Note - Sun is 0 & Sat is 6
cal.sMth = parseInt(cal.hMth.value); // selected month
cal.sYear = parseInt(cal.hYear.value); // selected year
let daysInMth = new Date(cal.sYear, cal.sMth+1, 0).getDate(), // number of days in selected month
startDay = new Date(cal.sYear, cal.sMth, 1).getDay(), // first day of the month
endDay = new Date(cal.sYear, cal.sMth, daysInMth).getDay(), // last day of the month
now = new Date(), // current date
nowMth = now.getMonth(), // current month
nowYear = parseInt(now.getFullYear()), // current year
nowDay = cal.sMth==nowMth && cal.sYear==nowYear ? now.getDate() : null ;
// (C2) LOAD DATA FROM LOCALSTORAGE
cal.data = localStorage.getItem("cal-" + cal.sMth + "-" + cal.sYear);
if (cal.data==null) {
localStorage.setItem("cal-" + cal.sMth + "-" + cal.sYear, "{}");
cal.data = {};
} else { cal.data = JSON.parse(cal.data); }
// (C3) DRAWING CALCULATIONS
// Blank squares before start of month
let squares = [];
if (cal.sMon && startDay != 1) {
let blanks = startDay==0 ? 7 : startDay ;
for (let i=1; i<blanks; i++) { squares.push("b"); }
}
if (!cal.sMon && startDay != 0) {
for (let i=0; i<startDay; i++) { squares.push("b"); }
}
// Days of the month
for (let i=1; i<=daysInMth; i++) { squares.push(i); }
// Blank squares after end of month
if (cal.sMon && endDay != 0) {
let blanks = endDay==6 ? 1 : 7-endDay;
for (let i=0; i<blanks; i++) { squares.push("b"); }
}
if (!cal.sMon && endDay != 6) {
let blanks = endDay==0 ? 6 : 6-endDay;
for (let i=0; i<blanks; i++) { squares.push("b"); }
}
// (C4) DRAW HTML CALENDAR
// Get container
let container = document.getElementById("cal-container"),
cTable = document.createElement("table");
cTable.id = "calendar";
container.innerHTML = "";
container.appendChild(cTable);
// First row - Day names
let cRow = document.createElement("tr"),
days = ["Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"];
if (cal.sMon) { days.push(days.shift()); }
for (let d of days) {
let cCell = document.createElement("td");
cCell.innerHTML = d;
cRow.appendChild(cCell);
}
cRow.classList.add("head");
cTable.appendChild(cRow);
// Days in Month
let total = squares.length;
cRow = document.createElement("tr");
cRow.classList.add("day");
for (let i=0; i<total; i++) {
let cCell = document.createElement("td");
if (squares[i]=="b") { cCell.classList.add("blank"); }
else {
if (nowDay==squares[i]) { cCell.classList.add("today"); }
cCell.innerHTML = `<div class="dd">${squares[i]}</div>`;
if (cal.data[squares[i]]) {
cCell.innerHTML += "<div class='evt'>" + cal.data[squares[i]] + "</div>";
}
cCell.onclick = () => { cal.show(cCell); };
}
cRow.appendChild(cCell);
if (i!=0 && (i+1)%7==0) {
cTable.appendChild(cRow);
cRow = document.createElement("tr");
cRow.classList.add("day");
}
}
// (C5) REMOVE ANY PREVIOUS ADD/EDIT EVENT DOCKET
cal.close();
}
cal.list()
draws the calendar for the currently selected month/year, and it is the most complicated function in this project. Long story short:
- (C1 & C2) The events data is kept in the
localStorage
in a monthly manner (cal-MONTH-YEAR = JSON ENCODED OBJECT
). - (C1 & C2) Retrieve the events data for the selected month/year, and put it into
cal.data
. - (C3) Do all the calculations in
squares = []
first. - (C4) Then, loop through
squares
to draw the HTML calendar.
PART 4) SHOW/EDIT CALENDAR EVENT
// (D) SHOW EDIT EVENT DOCKET FOR SELECTED DAY
show : (el) => {
// (D1) FETCH EXISTING DATA
cal.sDay = el.getElementsByClassName("dd")[0].innerHTML;
let isEdit = cal.data[cal.sDay] !== undefined ;
// (D2) UPDATE EVENT FORM
cal.hfTxt.value = isEdit ? cal.data[cal.sDay] : "" ;
cal.hfHead.innerHTML = isEdit ? "EDIT EVENT" : "ADD EVENT" ;
cal.hfDate.innerHTML = `${cal.sDay} ${cal.mName[cal.sMth]} ${cal.sYear}`;
if (isEdit) { cal.hfDel.classList.remove("ninja"); }
else { cal.hfDel.classList.add("ninja"); }
cal.hForm.classList.remove("ninja");
},
// (E) CLOSE EVENT DOCKET
close : () => {
cal.hForm.classList.add("ninja");
}
When the user clicks on a date cell, cal.show()
will be called. Pretty self-explanatory – We fetch the event from cal.data
, then show the add/edit event form.
PART 5) SAVE/DELETE EVENTS DATA
// (F) SAVE EVENT
save : () => {
cal.data[cal.sDay] = cal.hfTxt.value;
localStorage.setItem(`cal-${cal.sMth}-${cal.sYear}`, JSON.stringify(cal.data));
cal.list();
return false;
},
// (G) DELETE EVENT FOR SELECTED DATE
del : () => { if (confirm("Delete event?")) {
delete cal.data[cal.sDay];
localStorage.setItem(`cal-${cal.sMth}-${cal.sYear}`, JSON.stringify(cal.data));
cal.list();
}
Lastly, these are self-explanatory functions again – We simply update the events data in localStorage
.
USEFUL BITS & LINKS
Finally, here are a few more extras that may be useful.
MULTIPLE EVENTS PER DAY?
Of course, we can. Let’s take a look at the current save()
function:
cal.data[cal.sDay] = cal.hfTxt.value;
We are pretty much storing the event data in data[DAY] = EVENT
now. So to allow multiple events on the same day, the general idea is to change the current structure into a multi-dimensional array data[DAY] = [EVENT, EVENT, EVENT]
.
cal.data[cal.sDay] = [];
cal.data[cal.sDay].push(document.getElementById("evt-details").value);
Then, here comes the big problem. We have to redo all the rest of the functions to properly draw multiple events and manage them. As you can guess, that will increase the complexity exponentially, turning this into a “not simple tutorial”.
This is why I don’t answer the “support multiple events” and “span an event across multiple days” questions – Feel free to challenge yourself though, this simple calendar is a good starting point.
LIMITATIONS
- Only one event is allowed in a day.
- The events cannot span across multiple days.
- The Javascript calendar requires the use of local storage. If it is disabled on the browser or not supported, then it will not be able to save the events at all.
But of course, I have released this as an open-source. So feel free to tweak it however you see fit.
LINKS & REFERENCES
- Localstorage – MDN
- Date – MDN
- Calendar with PHP MySQL – Code Boxx
- Calendar Progress Web App – Code Boxx
THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
I need help with a simple event calendar . I dont have any skills at all. Most of the wordpress widgets are too huge and slow my website.
Hire a freelancer to help you turn this into a custom plugin then. 🙂
P.S. You should be looking at https://code-boxx.com/simple-php-calendar-events/, not this Javascript version.
P.P.S. Alternatively, just use Google Calendar and embed it into your site.
https://code-boxx.com/faq/#help Going to mark this as “irrelevant to the tutorial”.
Would it be possible to have the current date cell be colored in a different color? I am trying to do it myself but my javascript skills are very basic. Thanks.
Updated – Adapted “highlight today” from my PHP calendar.
Check out my progressive web app (PWA) version if you want. This one is a little more complicated, but it uses an indexed database, allows event date range, and has full offline capabilities – https://code-boxx.com/javascript-calendar-pwa/
Hi, thanks for sharing your work, I find it useful.
Regarding multiple events, in my case all I did was something like:
1. This is an event <br>
2. This is another event
I dont need more than that.
Thanks again
Greetings and thanks for the tutorial!
Cool
Wondering if you could help out on how to push the data from LocalStorage to Firebase?
Create an API endpoint to receive the data at Firebase, things can get complicated depending on your setup. Can’t work free for your personal project, good luck – https://code-boxx.com/faq/#help
Thanks for that. Thats great!
= LINK REMOVED = Sorry mate, nice plugin, but not a related tutorial. https://code-boxx.com/faq/#nolink
Awesome! Thank you for taking the time creating this!:D
How to make the calendar start on monday instead of sunday?
Updated the code – Just needed a small tweak to the date calculations.