DEV Community

Kim Heidorn
Kim Heidorn

Posted on

Making a Calendar in Vanilla Javascript

Arguably, time is the best and worst built-in function in Javascript. For my most recent project, I had the idea to make a bastardized Google calendars. I naively figured that making a general, functional calendar would be straightforward and allow me plenty of time to build out my site. I so was wrong.

Where to Start?

Pretty early on, with guidance from my instructor, I decided to hard code a month into the HTML. This allowed me to build out the general structure and style I wanted for my calendar. There was conflicting literature on whether using <div> or <table> would be better practice. I ultimately decided to go with a table.

If you have ever taken the time to really study a month calendar, the structure is pretty simple. There is a header at the top, usually, that indicates the month and year. Below that are 7 columns outlining the individual days of the week. Finally, there is a maximum of 6 rows to populate the number of days for a given month.

Below is a snapshot of my hardcoded HTML.

Hard-coded HTML Calendar

Again, this allowed me to mess with the CSS style sheet to render the overall look and feel I wanted for my calendar.

Harnessing the Power of Time

Now came the hard part. I needed to use the built-in time function to populate the accurate number of days in a given month and year. After doing some research, I learned that Javascript will return a number that corresponds to a months relative index in a year: i.e. January is 0, February is 1, etc. This can be called using the getMonth() function built-in to Javascript.

Javascript also does the same thing with days of the week and starts with Sunday. That makes Sunday - 0, Monday - 1, etc. This is called using the getDay() function from Javascript.

So, the question became how do I use this two crucial pieces of information: 1. How do I figure out what day of the week is the start of the month? 2. How do I calculate how many days are in a month?

Answering the first questions was simple enough. Javascript literature helped outline the various options for built-in functions. Miraculously, new Date(year, month) is incredibly powerful and will automatically render the first day of a given year and month argument. Using the .getDay() function on this allowed me to get the day index for the start of the month.

Start of Month Code

Next, I needed to get the number of days in a given month. Again, I used the all-powerful new Date() to get me started, and using some high school algebra, I reverse engineered a way to calculate the total days. The new Date() function can accept a large number of arguments, down to the millisecond. I did not need to get granular with my calendar, but thought I could use the month, year and day arguments. Given that the maximum number of days in any month is 31, putting in 32 days into new Date() would give me the relative date that would actually be in the next month. For example, July(index 6 of a year) has 31 days in a month. If I entered new Date(2019, 6, 32) into my console, I would expect the console to return August 1, 2019.
July Snapshot of Console

The new Date() function also has a getDate() function that will return the day number from a given date. Using this returned number, I subtracted this from 32 to get the accurate number of days in a given month.

End of Month Code

To further prove my concept, I used February 2019 in my console. Passing in 2019 for year, 1 for month, and 32 for days, I should expect the above equation to return 28, as there were 28 days in February 2019.

Feb Snapshot Console

One hurdle down, now I had to use this data to populate and render my table.

Populating the Beast

After removing the hardcoded HTML data from my table, I needed to populate the month span and the year span with the currently rendered month and year. This was fairly straightforward as I left a majority of my HTML code in the header. After finding the individual span elements using document.findElementById(), I changed the textContent to the given month and year.

Populating Month Year

After grabbing the table element, again using document.getElementById(), I then needed to add some for loops to render my rows and columns. I also knew that I would need to print the actual day number on the calendar. For obvious reasons, I started the count at 1 and assigned this 1 to a usable variable called renderNum. (Note: This variable will be incremented up by one with each column loop. But I’ll get to that in a bit)

Get The Variables

Next, I generated the row for loop, as this was going to hold all the individual date tiles. (To be more proper, I will append my row <tr> with my table data <td> elements.) Since I knew that I would need at maximum 6 rows, I started my for loop at the traditional 0 and had it stop when i reached 6. Seeing as there was no text content for the row, all I needed was to create the <tr> element and assign this to a usable variable.

First For Loop

As stated before, I wanted to append each row with 7 table data elements. This meant that I needed a nested for loop. This 2nd for loop also started at 0 and ended when the count (c in this case) reached 7. This was simple enough, but the next step took some logical reasoning. Remembering that the new Date(month, year).getDay() gave me the index of day of the week, I needed to setup empty <td>s so that the count and calendar rendering started on the correct day. To do this, I added an if statement in my column for loop that would check if: 1. The loop was on the first row (aka i===0), and 2. The value of count loop was less than the new Date(month, year).getDay() value (aka c < startOfMonth).

If both those conditions were true, I wanted the loop to create a <td> element, add an empty class to the newly rendered <td> and append this to the current row element.

Empty Column Data

Next, I wanted to make sure that I was populating the correct day number on each one of the new table data elements. Modifying my empty td element method, I added a <td> element for each new day, added the textContent, and appended the row. The textContent in this case would be the renderNum variable defined earlier. To make sure that the renderNum printed the correct date, I incremented the variable by 1.

Populated Column Data

Using the power of truthiness, I used an else if statement to stop the calendar from incrementing and adding data past the last day of the month.

Else If Statement

Once I reached the break and was out of the for loops, I needed to make sure that to append the table body with the newly formed and populated row. (I did rearrange and group the code according to variables, and my personal preference. Obviously, this is not necessary. It was just a personal preference.)

Final Code

Finally, I added EventListeners on the click of the arrows to toggle to the month before and after a calendar snapshot. Given the length of this post already, I will spare you. But there is plenty of literature on EventListeners that I recommend reading if you have trouble with that step.

Below is a snapshot of the rendered calendar. I will be sure to update this post with the github link, once the project is complete.

Next up, appointments, time zones and scheduling. Wish me luck!

Final Calendar

Top comments (14)

Collapse
 
jamesthomson profile image
James Thomson

A good read. Nice to see how you worked through each step of the process. One comment on your post, it would be great to format your code with markdown instead of using screenshots. Cheers

Collapse
 
aligear profile image
aligear

I think Carbon is better.

Collapse
 
knheidorn profile image
Kim Heidorn • Edited

Thanks to both of you for the suggestions - I'm an uber noob so for sure need the help to make my posts more readable. Cheers!

Collapse
 
intrnl profile image
intrnl

It looks nice but people with their own font sizes will have trouble with it.

Collapse
 
ajnasz profile image
Lajos Koszti

No, it's not.

Collapse
 
ddamato profile image
Donnie D'Amato

FYI, a single line to get the number of days in a month. new Date(year, month+1, 0).getDate(); where month represents a zero-indexed value for the month (January === 0, same as you are expecting above).

Collapse
 
preddys75 profile image
Scott Preddy

initially confused that you had typo putting number 6 in date call, but remembered index starts at 0 so its good.

For example, July(index 6 of a year) has 31 days in a month. If I entered new Date(2019, 6, 32) into my console, I would expect the console to return August 1, 2019

Collapse
 
karataev profile image
Eugene Karataev

Making a calendar is a really great exercise to train your js/html/css skills, thanks for sharing your process. I'd like to take a look at the result.
Btw, might be a good example of UI for inspiration.

Collapse
 
cre3z profile image
Cre3z • Edited

Hey Kim,

Thanks for the cool read, I recently did this as an exercise as well to test my skills. I would love to hear your comments on my code and you can check it out here :) I used ul/li tags instead and my vanilla js looks a bit different I tried to implement more ES6 syntax as well.

codesandbox.io/s/03kl637k7n?fontsi...

Collapse
 
theodesp profile image
Theofanis Despoudis

Thats a really good exercise

Collapse
 
talgat profile image
Talgat Sarybaev

Thank you, Kim! I really like articles which teaches smth specific. Tired of articles like '10 methods of Arrays', 'ES6 features that you will love', etc. Read the docs.

Collapse
 
prince_charm55 profile image
Prince Christian

where to find your full code

Collapse
 
aaryansharma444 profile image
aaryansharma444

why not use a printable calendar ?

Collapse
 
yota981 profile image
Yotaan

You can also try Print The Calendar, I'm Developer of this Website. I made it for Free for all.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.