2022-01-01

1641013200
guides
 01 Jan 2022  guides

Making a Weekly Habit Calendar with Bootstrap and JavaScript

I think one of the most important parts of ensuring that good habits become second nature is being able to schedule them in an organized manner — reducing cognitive load and making it easier to execute those habits on a recurring basis.

Recently I’ve created a simple webpage that acts as a weekly calendar of recurring habits, that I’m able to customize, deploy (using Netlify), and view on my mobile phone on any given day. In this blog post I’ll go over the steps and scaffolding needed to quickly create such a calendar.

If you’re impatient, there’s a Demo Repository that contains the HTML that you can immediately use.

Creating the Layout

There are many popular CSS frameworks for prototyping layouts in a few minutes. For this project I chose to use Bootstrap. The provided boilerplate on the Bootstrap website is all that’s needed, since we’ll be using Bootstrap via CDN

Below is a trimmed down version that just includes the CSS:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <title>Weekly Schedule</title>
  </head>
  <body>

  </body>
</html>

I won’t go into using Bootstrap itself, but any Bootstrap layout basically observes the following logical hierarchy as a best-practice:

<container>
  <row>
    <column>
    </column>
  </row>
<container>

Of course, Bootstrap isn’t that semantic out of the box; instead opting for CSS utility classes to achieve its structure.

Since we’ll be creating a weekly calendar, the goal is to have seven columns that will align differently based on device viewport (e.g., single-column on mobile) within a single row, encapsulated in a single container resulting a clean, neat layout.

Below is an example of the markup to be placed within the boilerplate’s <body> tags:

<div class="container-fluid">
  <div class="row p-3">

    <div class="col-lg-3 p-3">
      <p class="fs-4">Sunday</p>
      <div class="card" id="sunday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
          <li class="list-group-item">Morning Habit Two</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
        </ul>
      </div>
    </div>

    <div class="col-lg-3 p-3">
      <p class="fs-4">Monday</p>
      <div class="card" id="monday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
        </ul>
      </div>
    </div>

    <div class="col-lg-3 p-3">
      <p class="fs-4">Tuesday</p>
      <div class="card" id="tuesday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
        </ul>
      </div>
    </div>

    <div class="col-lg-3 p-3">
      <p class="fs-4">Wednesday</p>
      <div class="card" id="wednesday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
          <li class="list-group-item">Evening Habit Two</li>
        </ul>
      </div>
    </div>

    <div class="col-lg-3 p-3">
      <p class="fs-4">Thursday</p>
      <div class="card" id="thursday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
        </ul>
      </div>
    </div>

    <div class="col-lg-3 p-3">
      <p class="fs-4">Friday</p>
      <div class="card" id="friday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
        </ul>
      </div>
    </div>

    <div class="col-lg-3 p-3">
      <p class="fs-4">Saturday</p>
      <div class="card" id="saturday">
        <div class="card-header fw-light text-uppercase small">Morning</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Morning Habit One</li>
          <li class="list-group-item">Morning Habit Two</li>
        </ul>
        <div class="card-header fw-light text-uppercase small">Evening</div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Evening Habit One</li>
        </ul>
      </div>
    </div>

  </div>
</div>

A few notes on the above Bootstrap classes:

  • container-fluid is used for a container with more full-width viewport spacing out of the box
  • row p-3 sets the overall row padding using Bootstrap’s spacing utility classes on the sole row that is being used
  • col-lg-3 p-3 sets the overall column padding, and matches Bootstrap’s breakpoint for large screens to display four (4) columns on desktop, and single-column on smaller screen sizes
  • class="card" uses Bootstrap’s card element with accompanying list group structure

Also important to note is that we’ve added an id to each card element corresponding to the day of the week (e.g. id="wednesday"). This is done to enable some JavaScript tweaks whenevre the day’s card will need to be referenced.

Highlighting the Current Day

It would be nice if the current day could be highlighted. At least this was my thought process after creating the layout — since I would be accessing this almost exclusively from my phone — thus being able to automatically scroll and have the current day of the week visually indicated would be a plus.

The JavaScript to do this is trivial, so let’s go over the steps. First, I created an array to store the days of the week (that match each id that was created in the HTML markup):

const daysOfWeek = ["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];

Next, I used the standardized JavaScript Date() constructor and getDay() method to assign a value that matches the current day.

In the below expression, the variable d is assigned the value of the current date, as generated by calling the Date() constructor with the new operator:

const d = new Date();

Next, the getDay() method can be called on the recently created object d that contains today’s date. When this is done, getDay() will return an integer from 0 - 6 that matches the numerical value of whatever day of the week it is:

daysOfWeek[d.getDay()];
// daysOfWeek[1] if it's Monday

Then, the variable day is assigned the value of whatever index of the array daysOfWeek is when using the index number that’s returned by getDay():

let day = daysOfWeek[d.getDay()];
/*
d.getDay() === 1 if it's Monday
day === 'monday'' if it's Monday
*/

daysOfWeek[1] will be monday, daysOfWeek[3] will be wednesday and so on, matching the current day. More importantly, the variable day now matches the id value that each card element possesses.

Now that we a variable that holds the value of the current day, we find and manipulate the corresponding HTML element as needed:

let dayCard = document.getElementById(day);
// dayCard === '<div class="card" id="monday">' if it's Monday

In the above expression, the variable dayCard is assigned the value of the HTML card element whose id matches the current day.

We can then add some handy Bootstrap classes to make the day stand out, such as a red border:

dayCard.classList.add("border-danger","border-2");
// dayCard === <div class="card border-danger border-2" id="monday">

Further, we can make the title of the curent day stand out as well:

let dayTitle = dayCard.previousElementSibling;
// dayTitle === '<p class="fs-4">Monday</p>' if it's Monday

dayTitle.classList.add("fw-bold","text-danger");
// dayTitle === '<p class="fs-4 fw-bold text-danger">Monday</p>'

In the above snippet, previousElementSibling gets the previous element in the same tree level (the sibling), i.e. the previous <p> element that contains the day’s title. This element’s value is assigned to dayTitle, and then the necessary classes are simply added.

Finally, it would be extremely handy on a mobile device if the page can automatically scroll to the current day. While this isn’t necessarily useful on desktop screens, as I mentioned earlier I primarily use this weekly habit calendar on my phone.

This is easily done using the scrollIntoView() web API:

dayTitle.scrollIntoView();

Putting it all together, the complete markup is as follows:

<script>
  const daysOfWeek = ["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];

  const d = new Date();
  let day = daysOfWeek[d.getDay()];

  let dayCard = document.getElementById(day);
  dayCard.classList.add("border-danger","border-2");

  let dayTitle = dayCard.previousElementSibling;
  dayTitle.classList.add("fw-bold","text-danger");

  dayTitle.scrollIntoView();
</script>

It’s important to place this script tag and content below the closing Bootstrap container div tag; since the HTML will need to be rendered before each day’s id can be targeted using JavaScript.

At this point, simply entering details into each list group item and viewing the webpage will show a neat calendar already populated, that can be deployed to any static host such as Netlify, Amazon S3, Vercel, etc.

You can check out a Live Demo at the following URL: weekly-habit-calendar.netlify.app

Copyright © Paramdeo Singh · All Rights Reserved · Built with Jekyll

This node last updated November 7, 2023 and is permanently morphing...

Paramdeo Singh Guyana

Generalist. Edgerunner. Riding the wave of consciousness in this treacherous mortal sea.

Technology Design Strategy Literature Personal Blogs
Search Site

Results are from Blog, Link Dumps, and #99Problems