Calendar with JavaScript

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

We can do so many things with the JavaScript programming language and I enjoy building things with it, this time, I made a calendar of 12 months.
I will walk you through the steps on how I created this calendar using JavaScript.

Table of contents:

  1. Demo
  2. HTML structure
  3. Styles with CSS
  4. JavaScript functionalities

1. Demo

2. HTML structure

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Calendar</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Kalam&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
   <div id="input-container">
      <p>Select a year:</p>
      <input type="number" id="year" name="year" />
    </div>
    <div id="table"></div>
    <script src="./index.js" type="module"></script>
  </body>
</html>

In the head element, I'm importing a font family called Kalam which I will use in my styles. After that I'm connecting my CSS file, the link of the font family must come first, otherwise, it would not show inside of our CSS file.
In the body element, I have an input field where we can change the year. By default, the input field has the value of the current year. After that, I have a div with an id called table where I will append all the 12 tables.
At the end, I connect the index.js file which has the attribute type module, since I'm storing the days of the week and the months arrays in separate files.

3. Styles with CSS

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Kalam', cursive;
}

body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 20px;
  padding: 100px 0;
  background: #e5e3c9;
}

#table {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(3, 1fr);
  column-gap: 20px;
  row-gap: 20px;
  width: 95%;
  max-width: 1600px;
  justify-items: center;
}

#input-container {
  display: flex;
  align-items: center;
  gap: 20px;
  width: 90%;
  max-width: 800px;
}

#input-container > p {
  font-size: 1.2rem;
  color: #10422b;
}

input {
  background: #b4cfb0;
  border: none;
  padding: 10px;
  border-radius: 8px;
  color: #10422b;
  outline: none;
  font-size: 1rem;
}

table {
  border-collapse: collapse;
  background: #b4cfb0;
}

tr,
th,
td {
  text-align: center;
}

th {
  background: #94b49f;
  padding: 20px 10px;
  font-size: 1.3rem;
  color: #10422b;
}

@media (max-width: 600px) {
  body {
    padding: 50px 0;
    height: auto;
    gap: 40px;
  }

  #input-container {
    flex-direction: column;
    gap: 15px;
    margin-left: 0;
  }

  input,
  select {
    width: 100%;
  }

  th {
    font-size: 1.1rem;
    padding: 10px 0;
  }

  .week {
    width: auto;
  }

  .days {
    font-size: 1.1rem;
    height: 60px;
  }
}

I'm resetting my CSS at the beginning of the file, so the elements can start with 0 padding and margin. With this, I can achieve the same styles across browsers because each browser has different default styles for the elements. I also set the font family Kalam for everything and a fallback font cursive.
I center everything on the page and I gave the body a height of 100vh.
The main container with the id table displays the 12 months in a grid layout with 4 columns and 3 rows.

  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(3, 1fr);
  column-gap: 20px;
  row-gap: 20px;

For the tables, I set the border-collapse property to collapse, so we don't see the borders of the cells.

border-collapse: collapse;

I used the same colors for both the input div and the table so everything can have the same styles.
At the end of the file, I added a media query for screens below 600px so they can fit nicely on mobile as well.

4. JavaScript functionalities

In a separate folder, I have the days of the week array:

const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

export default daysOfWeek;

And the months array:

const months = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December',
];

export default months;

So the first thing I'm doing in the index.js file is that I'm importing both arrays and then setting all the variables that I will need throughout the program.

import months from './modules/months.js';
import daysOfWeek from './modules/daysOfWeek.js';

const yearInput = document.getElementById('year');
const currentYear = new Date().getFullYear();
const mainContainer = document.getElementById('table');
let year;

Then, I add an event listener to the input field so we can change the year if we want. This callback function will set the year to the input value, clear the main container and generate the calendars again.

yearInput.addEventListener('change', () => {
  year = yearInput.value;
  mainContainer.innerHTML = '';
  generateCalendars();
});

By default, we have the current year as the value.

yearInput.value = currentYear;
year = yearInput.value;

After that, I created a function which generates the calendars:

const generateCalendars = () => {
  const createDays = (month) => {
    const daysContainer = document.getElementById(`${month}-days`);
    let daysId = 1;
    for (let i = 0; i < 6; i++) {
      const week = document.createElement('tr');
      week.style.height = '50px';
      for (let j = 0; j < 7; j++) {
        const day = document.createElement('td');
        day.setAttribute('class', 'days');
        day.setAttribute('id', `${month}-${daysId++}`);
        week.appendChild(day);
      }
      daysContainer.appendChild(week);
    }
  };
  
  // Set the number of the days
  const setDays = (month) => {
    // Get first day of month
    const firstDay = new Date(year, months.indexOf(month), 1)
      .toDateString()
      .split(' ')[0];

    // Get last day of month
    const lastDay = new Date(year, months.indexOf(month) + 1, 0)
      .toDateString()
      .split(' ')[2];
    let startDay;

    switch (firstDay) {
      case 'Sun':
        startDay = 0;
        break;
      case 'Mon':
        startDay = 1;
        break;
      case 'Tue':
        startDay = 2;
        break;
      case 'Wed':
        startDay = 3;
        break;
      case 'Thu':
        startDay = 4;
        break;
      case 'Fri':
        startDay = 5;
        break;
      case 'Sat':
        startDay = 6;
        break;
      default:
        startDay = 0;
    }

    let daysCount = 1;
    for (let x = 1; x <= Number(lastDay); x++) {
      const d = document.getElementById(`${month}-${startDay + 1}`);
      d.textContent = daysCount++;
      startDay++;
    }
  };

  // Create 12 tables
  months.forEach((month) => {
    const div = document.createElement('div');
    div.setAttribute('class', 'calendars');
    div.innerHTML = ` <table>
  <thead>
    <tr>
      <th class="year-display" colspan="7">
        <p>
          <span id=${month}-span class="month-span">${month}</span>
        </p>
      </th>
    </tr>
    <tr id=${month} class="weeksContainer"></tr>
  </thead>
  <tbody id=${month}-days class="daysContainer"></tbody>
</table>`;

    mainContainer.appendChild(div);

    // Create the days of the week
    const weekRow = document.getElementById(`${month}`);
    daysOfWeek.forEach((day) => {
      const dayHeader = document.createElement('th');
      dayHeader.textContent = day;
      dayHeader.setAttribute('class', 'week');
      weekRow.appendChild(dayHeader);
    });
    createDays(month);
    setDays(month);
  });
  generateCalendars();

Let's break up the function.
First, we create 12 tables:

// Create 12 tables
  months.forEach((month) => {
    const div = document.createElement('div');
    div.setAttribute('class', 'calendars');
    div.innerHTML = ` <table>
  <thead>
    <tr>
      <th class="year-display" colspan="7">
        <p>
          <span id=${month}-span class="month-span">${month}</span>
        </p>
      </th>
    </tr>
    <tr id=${month} class="weeksContainer"></tr>
  </thead>
  <tbody id=${month}-days class="daysContainer"></tbody>
  </table>`;

  mainContainer.appendChild(div);

For each month I create a div, set a class called calendars, and set the inner HTML to be a table element. Then each div is appended to the main container.
After this, I create the days of the week for each month:

 // Create the days of the week
    const weekRow = document.getElementById(`${month}`);
    daysOfWeek.forEach((day) => {
      const dayHeader = document.createElement('th');
      dayHeader.textContent = day;
      dayHeader.setAttribute('class', 'week');
      weekRow.appendChild(dayHeader);
    });
    createDays(month);
    setDays(month);
  });

I get the element (weekRow) from the tables where I will append the 7 days. For each day, I create a th element, set the text content to the same (name of the day), set a class attribute called week, and append them in the weekRow element.
At the end of the function, I call two other functions createDays which will create the days of the months and the setDays which will set the numbers on each day.

 // Create the days of the month
  const createDays = (month) => {
    const daysContainer = document.getElementById(`${month}-days`);
    let daysId = 1;
    for (let i = 0; i < 6; i++) {
      const week = document.createElement('tr');
      week.style.height = '50px';
      for (let j = 0; j < 7; j++) {
        const day = document.createElement('td');
        day.setAttribute('class', 'days');
        day.setAttribute('id', `${month}-${daysId++}`);
        week.appendChild(day);
      }
      daysContainer.appendChild(week);
    }
  };

I create 6 rows in each month and give each day an id which starts from 1 and is incremented at each iteration. So for example, if we have a month of 30 days, then the days will have an id from 1-30.
And finally, the function for setting the number of the days:

// Set the number of the days
  const setDays = (month) => {
    // Get first day of month
    const firstDay = new Date(year, months.indexOf(month), 1)
      .toDateString()
      .split(' ')[0];

    // Get last day of month
    const lastDay = new Date(year, months.indexOf(month) + 1, 0)
      .toDateString()
      .split(' ')[2];
    let startDay;

    switch (firstDay) {
      case 'Sun':
        startDay = 0;
        break;
      case 'Mon':
        startDay = 1;
        break;
      case 'Tue':
        startDay = 2;
        break;
      case 'Wed':
        startDay = 3;
        break;
      case 'Thu':
        startDay = 4;
        break;
      case 'Fri':
        startDay = 5;
        break;
      case 'Sat':
        startDay = 6;
        break;
      default:
        startDay = 0;
    }

    let daysCount = 1;
    for (let x = 1; x <= Number(lastDay); x++) {
      const d = document.getElementById(`${month}-${startDay + 1}`);
      d.textContent = daysCount++;
      startDay++;
    }
  };

I need the first and the last day of each month, so I can loop until the last day. Also, I set a variable startDay which will vary according to the first day. For example, if the first day of the month is a Wednesday, then the startDay will have the value of 3 and we start adding the numbers from this day:

const d = document.getElementById(`${month}-${startDay + 1}`);
d.textContent = daysCount++;

So I'm getting the day with the id that starts from the first day and I will increment it by 1 at each iteration. Since the week starts with Sunday, I need to add 1 to the id, so the first day which is a Wednesday would be startDay + 1 which is (3 + 1). Wednesday has the value number 3, adding 1, it will target the day with the id 4.
We need to call the whole function in the end of the file, so the calendars will generate on page load.

generateCalendars();

Conclusion

In my opinion, this is a great project for beginners to try out. We could have created the day cells, and the days of the week in the HTML file but I wanted to show you a way where we can generate them with JavaScript.
You can check out the live version here: Calendar with JavaScript

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.