DEV Community

Cover image for Building a simple calendar with Vanilla JS
EliHood
EliHood

Posted on • Edited on

Building a simple calendar with Vanilla JS

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>


Enter fullscreen mode Exit fullscreen mode

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;
}


Enter fullscreen mode Exit fullscreen mode

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",
];


Enter fullscreen mode Exit fullscreen mode

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. 

}


Enter fullscreen mode Exit fullscreen mode

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;

  ....
  }



Enter fullscreen mode Exit fullscreen mode

Image description




...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";
      }
    }
  }


Enter fullscreen mode Exit fullscreen mode

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();


Enter fullscreen mode Exit fullscreen mode

Image description

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");
    }



Enter fullscreen mode Exit fullscreen mode

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.

Image description

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();


Enter fullscreen mode Exit fullscreen mode

Demo

Image description

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)

Top comments (0)