Create a Responsive Calendar with Vanilla Javascript and CSS Grid
Tags: javascript, webdev
Published: 2020-09-02 16:56:41

---
title: Create a Responsive Calendar with Vanilla Javascript and CSS Grid
published: true
description: A responsive calendar developed in vanilla javascript and css grid 
tags: javascript, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/xhkrjyo4smsjw35ylgxd.png
---
In this post, I attempt to show how to develop a calendar using vanilla Javascript and CSS Grid Layout. Following CodePen shows the final code.

{%codepen https://codepen.io/amitgupta15/pen/OJNjzNX default-tab=result%}

We start with an HTML page that has a single `<div>` element.
```html
<div data-app="calendar-app"></div>
```
We will use Javascript to build and render our calendar in this `<div>`.

In our Javascript file, we start by defining an array for the months and an array for days of the week.
```javascript 
var months = ['January','February','March','April','May','June','July','August','September','October','November','December']; 
var daysOfWeek = ['S','M','T','W','TH','F','SA'];
```
The application level `state` object stores the `month` and `year` being displayed. We initialize the `state` object with the current month and year.
```javascript
var state = {
  month: new Date().getMonth(),
  year: new Date().getFullYear()
}
```  

Now, let's take a look at `datesForGrid` function, which is the most important function of our application. We pass a `year` and `month` as arguments and the function returns an array of 42 entries to fill up the 7x6 date grid for the given year and month. **`month` is 0-indexed**. 

At the beginning of the function, we declare an empty `dates` array to store the date entries.
```javascript
//month is 0-indexed
function datesForGrid(year, month) {
  var dates= [];
  ...
}
```
`firstDay` stores the day of the week on which the month starts. 
```javascript
...
//Date.getDay() method is 0-indexed.
var firstDay = new Date(year, month).getDay();
...
```
If value of `firstDay` is 2, that means the month starts on a Tuesday (Sunday=0, Monday=1, and so on).

Next, we have to find out how many total days are in the month
```javascript
...
var totalDaysInMonth = new Date(year, month + 1, 0).getDate();
...
```
By incrementing the the `month` value by 1 and passing `0` for the date argument in the `Date()` function, we get the last date of the month in question and hence the total number of days in the month.

We are also interested in the total number of days in the previous month (you'll soon see why). 
```javascript
...
var totalDaysInPrevMonth = new Date(year, month, 0).getDate();
...
```
Again, by passing the current `month` value and `0` for the date argument in the `Date()` function we get the last day of the previous month.

Now, we start populating the `dates` array. 

First, we check how many dates from the previous month do we have to display in the calendar. For example, if the current month starts on a Tuesday, then we have to show 2 dates from the previous month for Sunday and Monday. 

We start by adding entries for the previous month in the `dates` array.
```javascript
...
// Days from prev month to show in the grid
for(var i = 1; i <= firstDay; i++) {
  var prevMonthDate = totalDaysInPrevMonth - firstDay + i;
  var key = new Date(state.year, state.month -1, prevMonthDate).toLocaleString();    
  dates.push({key: key, date: prevMonthDate, monthClass:'prev'});
}
...
```
An entry in `dates` array is an object. We store a `key` property which is a string representation of the date, `date` is the numerical date value, `monthClass:'prev'` indicates that the date belongs to the previous month. 

`var prevMonthDate = totalDaysInPrevMonth - firstDay + i` computes the numerical date value to be stored in the `date` property of the object. 
Let's say the current month is Sept 2020, which means the last month was Aug 2020. In that case, `totalDaysInPrevMonth = 31`. 
Since Sept 1st, 2020 will be a Tuesday, so `firstDay = 2`. Therefore, `prevMonthDate` value for `i = 1` will be `31 - 2 + 1 = 30`.

So, for Sept 2020, the first two entries in the `dates` array will be as follows
```javascript
//Example for September 2020
dates[0] = {key: '8/30/2020, 12:00:00 AM', date: 30, monthClass: 'prev'};
dates[1] = {key: '8/31/2020, 12:00:00 AM', date: 31, monthClass: 'prev'};
```
Next, we populate the `dates` array with entries for the current month
```javascript
...
// Days of the current month to show in the grid
var today = new Date();
for(var i = 1; i <= totalDaysInMonth; i++) {
  var key = new Date(state.year, state.month, i).toLocaleString();
  if(i === today.getDate() && state.month === today.getMonth() && state.year === today.getFullYear()) {
    dates.push({key: key, date: i, monthClass: 'current', todayClass: 'today'});
  } else{ 
    dates.push({key: key, date: i, monthClass: 'current'});
  }
}
...
``` 
Here we add an additional property `todayClass:'today'` to the object for today's date.

So, for Sept 1, 2020 the `dates` array will look like this
```javascript
//Example for September 2020
...
dates[2] = {key: '9/1/2020, 12:00:00 AM', date: 1, monthClass: 'current', todayClass:'current'};
dates[3] = {key: '9/2/2020, 12:00:00 AM', date: 2, monthClass: 'current'};
...
```
After adding all the entries for the month to be displayed, we check if there is still space left in the grid. If the size of `dates` array is less than 42, then we enter the dates for the next month in the `dates` array.
```javascript
...
var gridsize = 42;
// If there is space left over in the grid, then show the dates for the next month
if(dates.length < gridsize) {
  var count = gridsize - dates.length;
  for(var i = 1; i <= count; i++) {
    var key = new Date(state.year, state.month + 1, i).toLocaleString();
    dates.push({key: key, date: i, monthClass:'next'});
  }
}
...
```
`render()` function is responsible for rendering the calendar on the webpage. 
```javascript
function render() {  
  var calendarApp = document.querySelector('[data-app=calendar-app]');
  // Building the calendar app HTML from the data
  calendarApp.innerHTML = `
    <div class="calendar-nav">
      <button id="prev-month">Previous</button>
      <h2>${months[state.month]} ${state.year}</h2>
      <button id="next-month">Next</button>
    </div>
    <div class='calendar-grid'>
      ${ daysOfWeek.map(day => `<div>${day}</div>` ).join('') }
      ${ datesForGrid(state.year, state.month).map(date => `<div id="${date.key}" class="${date.monthClass} ${date.todayClass ? date.todayClass : ''}">${date.date}</div>`).join('') }
    </div>
  `;
}
```
`.calendar-grid` css class defines a simple grid layout for the calendar. Each grid cell is a `<div>`. Following is an example of rendered HTML
```html
<div class='calendar-nav'>
  <button id="prev-month">Previous</button>
  <h2>September 2020</h2>
  <button id="next-month">Next</button>
</div>
<div class='calendar-grid'>
  ...
  <div id="8/31/2020, 12:00:00 AM" class="prev">31<div>
  <div id="9/1/2020, 12:00:00 AM" class="current today">1<div>
  <div id="9/2/2020, 12:00:00 AM" class="current">2<div>
  <div id="9/3/2020, 12:00:00 AM" class="current">3<div>
  ...
  <div id="10/1/2020, 12:00:00 AM" class="next">1<div> 
  ...
</div>
```
`showCalendar()` function is the main entry point. It takes one numerical argument `prevNextIndicator`. 
```javascript
//valid prevNextIndicator values
-1 = previous month
0 = current month
1 = next month
```
`showCalendar()` function sets the `state` variable based on the value of `prevNextIndicator` and calls the `render()` function
```javascript
function showCalendar(prevNextIndicator) {
  var date = new Date(state.year, state.month + prevNextIndicator);
  //Update the state
  state.year = date.getFullYear();
  state.month = date.getMonth();  
  render();
}

// Show the current month by default
showCalendar(0);
```
Finally, we add event listeners for the Previous (<) and Next (>) buttons.
```javascript
document.addEventListener('click', function(ev) {
  if(ev.target.id === 'prev-month') {
    showCalendar(-1);
  }
  if(ev.target.id === 'next-month') {
    showCalendar(1);
  }
});
```

That's our calendar. With less than 100 lines of Javascript code and a few lines of CSS, we created a fully functional calendar.