Building a simple calendar with Vanilla JS
EliHood
Posted on November 26, 2023
In todays post, we'll be building a simple calendar using just vanilla JS. I will be using typescript, and using tsc to build out the index.js.
note: I'll be focusing more on the engine / loop part specifically, which i find to be the most important when it comes to this calendar algorithm.
The basis of making a calendar, requires two things really.
Lets make some generic html markup.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Some Calendar</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<div class="calendar-body">
<div id="header">
<h1 id="month-heading"></h1>
<div>
<button id="backButton">Back</button>
<button id="nextButton">Next</button>
</div>
</div>
<div id="weekdays">
<div>Sunday</div>
<div>Monday</div>
<div>Tuesday</div>
<div>Wednesday</div>
<div>Thursday</div>
<div>Friday</div>
<div>Saturday</div>
</div>
<div id="calendar"></div>
</div>
</div>
<script src="dist/index.js"></script>
</body>
</html>
Some CSS
body, html{
height:100%;
width:100%;
padding:0px;
margin:0px;
}
#month-heading{
padding: 0.1em;
font-size: 1.4em;
box-sizing: border-box;
}
.container{
display: flex;
justify-content: center;
width: 770px;
height: auto;
margin: 0 auto;
}
#header{
padding:10px;
display: flex;
justify-content: space-between;
}
.day + #currentDay{
background-color: #e8f4fa;
}
.prev-day{
background-color: #FFFCFF !important;
box-shadow: none !important;
}
#weekdays{
width: 100%;
display: flex;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
#weekdays div{
width: 100px;
padding: 10px;
}
.calendar-body{
background-color: #eee;
margin: auto;
width: 800px;
height: auto;
position: relative;
}
#calendar{
width: 100%;
margin: auto;
display: flex;
flex-wrap: wrap;
}
.day{
width: 100px;
padding: 10px;
height: 100px;
box-sizing: border-box;
margin: 5px;
background-color: #fff;
box-shadow: 0px 0px 3px #CBD4c2;
display: flex;
flex-direction: column;
justify-content: space-between;
}
So lets get into the JS part.
Getting the first day of the month
// specify the weekdays
const weekDays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
Setting up the Date Api.
function main(){
const dt = new Date();
const month = dt.getMonth(); // 10
const year = dt.getFullYear(); // 2023
// gets first day of month based on the above params
const firstDayOfMonth = new Date(year, month, 1); // Wed Nov 01 2023 00:00:00 GMT-0400 (Eastern Daylight Time)
// tolocaleString parse the above date in a readable format.
const dateString = firstDayOfMonth.toLocaleString("en-us", {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
}); // Wednesday, 11/1/2023
/**
* we want to get the first weekday of the month,
* so we have a string of Wednesday, 11/1/2023.
*
* We want just the Wednesday part... so were going to make this string into an array.
* so we split the string into array elements for every comma "," and leading white space.
* dateString.split(", ") which gives us the following:
*
* (2) ['Wednesday', '11/1/2023']
*/
const getFirstDayMonth = dateString.split(", ")[0]; // Wednesday
const days = weekDays.indexOf(getFirstDayMonth); // returns an index of 3 being that Wednesday is the 3rd array element.
}
So we have some of the basis for our calendar program.
Lets get into the looping aspect...
The Loop
...main function continued..
const daysInMonth = new Date(year, month + 1, 0).getDate(); // 30
/**
* lets then add days plus the days in the month so
*
* 3 + 30
*/
const daysAndMonthTotal = days + daysInMonth; // 33
/**
* we could create an array to reference its length
*/
const daysArr = new Array(daysAndMonthTotal); // 33
const day = dt.getDate(); // 26
for (let i = 1; i <= daysArr.length; i++) {
const square = document.createElement("div") as any;
square.classList.add("day");
/**
* here we are saying substract the index from the days.
*
*
* e.g
*
* Days will always be 3 in this case, and will always increment.
*
* So
* 1 - 3 = -2
* 2 - 3 = -1
* 3-3 = 0
* 4-3 = 1 // we start here
*
*/
const addedDay = i - days;
....
}
...continued
const addedDay = i - days;
// so we start when i is greater than 3.
if (i > days) {
square.innerText = addedDay;
// if addedDay is today, lets give it a special color.
if (addedDay === day) {
square.id = "currentDay";
}
}
}
The complete code so far with some javascript event listeners added and etc.
let nav = 0;
const getCalendar = document.getElementById("calendar") as any;
const weekDays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
function nextButton() {
document.getElementById("nextButton")?.addEventListener("click", () => {
nav++;
main();
});
}
function backButton() {
document.getElementById("backButton")?.addEventListener("click", () => {
nav--;
main();
});
}
function main() {
const dt = new Date();
if (nav !== 0) {
dt.setMonth(new Date().getMonth() + nav);
}
const month = dt.getMonth();
const year = dt.getFullYear();
const firstDayOfMonth = new Date(year, month, 1);
const dateString = firstDayOfMonth.toLocaleString("en-us", {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
});
const lastDayOfLastMonth = new Date(year, month, 0).getDate();
(
document.getElementById("month-heading") as any
).innerText = `${dt.toLocaleDateString("en-us", { month: "long" })} ${year}`;
getCalendar.innerHTML = "";
/**
* we want to get the first weekday of the month,
* so we have a string of Wednesday, 11/1/2023.
*
* We want just the Wednesday part... so were going to make this string into an array.
* so we split the string into array elements for every comma "," and leading white space.
* dateString.split(", ") which gives us the following:
*
* (2) ['Wednesday', '11/1/2023']
*/
const getFirstDayMonth = dateString.split(", ")[0]; // Wednesday
const days = weekDays.indexOf(getFirstDayMonth);
/**
* Lets get the days in the current month by the following
*/
const daysInMonth = new Date(year, month + 1, 0).getDate(); // 30
/**
* lets then add days plus the days in the a month so
*
* 3 + 30
*/
const daysAndMonthTotal = days + daysInMonth; // 33
/**
* we could create an array the reference its length
*/
const daysArr = new Array(daysAndMonthTotal); // 33
const day = dt.getDate(); // 26
/**
* We use the for loop to iterate through the daysArr
*
* If I = 1, and 1 is less than 33, render us some squares.. essentially
*/
for (let i = 1; i <= daysArr.length; i++) {
const square = document.createElement("div") as any;
square.classList.add("day");
/**
* here we are saying substract the index from the days.
*
*
* e.g
*
* Days will always be 3 in this case, and I will always increment.
*
* So
* 1 - 3 = -2
* 2 - 3 = -1
* 3-3 = 0
* 4-3 = 1 // we start here
*
*/
const addedDay = i - days;
/**
* if the index is greater than the first day of the month add text for numbers
*
* else add padding squares
*/
if (i > days) {
square.innerText = addedDay;
if (addedDay === day && nav === 0) {
square.id = "currentDay";
}
}
getCalendar?.appendChild(square);
}
}
nextButton();
backButton();
main();
Last thing, lets add the previous days of last month.
if (i > days) {
square.innerText = addedDay;
if (addedDay === day && nav === 0) {
square.id = "currentDay";
}
} else {
// enter the last few days of last month here
square.innerText = lastDayOfLastMonth - days + i;
square.classList.add("prev-day");
}
lastDayOfLastMonth
is the 31st of October. But were just getting the number not the month.
So we do 31 - 3 + i(1) = 29,
And then 31 - 3 + i(2) = 30, etc.
Completed Code
let nav = 0;
const getCalendar = document.getElementById("calendar") as any;
const weekDays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
function nextButton() {
document.getElementById("nextButton")?.addEventListener("click", () => {
nav++;
main();
});
}
function backButton() {
document.getElementById("backButton")?.addEventListener("click", () => {
nav--;
main();
});
}
function main() {
const dt = new Date();
if (nav !== 0) {
dt.setMonth(new Date().getMonth() + nav);
}
const month = dt.getMonth();
const year = dt.getFullYear();
const firstDayOfMonth = new Date(year, month, 1);
const dateString = firstDayOfMonth.toLocaleString("en-us", {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
});
const lastDayOfLastMonth = new Date(year, month, 0).getDate();
(
document.getElementById("month-heading") as any
).innerText = `${dt.toLocaleDateString("en-us", { month: "long" })} ${year}`;
getCalendar.innerHTML = "";
/**
* we want to get the first weekday of the month,
* so we have a string of Wednesday, 11/1/2023.
*
* We want just the Wednesday part... so were going to make this string into an array.
* so we split the string into array elements for every comma "," and leading white space.
* dateString.split(", ") which gives us the following:
*
* (2) ['Wednesday', '11/1/2023']
*/
const getFirstDayMonth = dateString.split(", ")[0]; // Wednesday
const days = weekDays.indexOf(getFirstDayMonth);
/**
* Lets get the days in the current month by the following
*/
const daysInMonth = new Date(year, month + 1, 0).getDate(); // 30
/**
* lets then add days plus the days in the a month so
*
* 3 + 30
*/
const daysAndMonthTotal = days + daysInMonth; // 33
/**
* we could create an array the reference its length
*/
const daysArr = new Array(daysAndMonthTotal); // 33
const day = dt.getDate(); // 26
/**
* We use the for loop to iterate through the daysArr
*
* If I = 1, and 1 is less than 33, render us some squares.. essentially
*/
for (let i = 1; i <= daysArr.length; i++) {
const square = document.createElement("div") as any;
square.classList.add("day");
/**
* here we are saying substract the index from the days.
*
*
* e.g
*
* Days will always be 3 in this case, and I will always increment.
*
* So
* 1 - 3 = -2
* 2 - 3 = -1
* 3-3 = 0
* 4-3 = 1 // we start here
*
*/
const addedDay = i - days;
/**
* if the index is greater than the first day of the month add text for numbers
*
* else add padding squares
*/
if (i > days) {
square.innerText = addedDay;
if (addedDay === day && nav === 0) {
square.id = "currentDay";
}
} else {
// enter the last few days of last month here.
square.innerText = lastDayOfLastMonth - days + i;
square.classList.add("prev-day");
}
getCalendar?.appendChild(square);
}
}
nextButton();
backButton();
main();
Demo
Conclusion
Hopefully this was helpful, cheers 🥳
Credits:
https://unsplash.com/photos/a-calendar-with-red-push-buttons-pinned-to-it-bwOAixLG0uc
https://youtu.be/m9OSBJaQTlM?si=WQQkJHeqD8yPmvQc (inspiration)
Posted on November 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.