🛠️ Datepicker: Building the calendar

Hey ,

I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.

Please upgrade (use this link) access this content.

I'm super eager to help you learn more!

🛠️ Datepicker: Building the calendar

We need to pass a Date object into the Datepicker if we want the it to work with any date. Let’s start by creating a new date.

const date = new Date()

You can use any date you want. For the sake of consistency, we will use 13 February 2019 for this datepicker.

// Remember, month is zero-indexed. 1 means February.
const date = new Date(2019, 1, 13)

We need to update the calendar based on the date given.

Once again, there are three parts to the calendar:

  1. The year-month indicator
  2. The day-of-week indicator
  3. The dates in the month

The year-month indicator

We want to create this HTML for the year-month indicator.

<div class="datepicker__year-month">
  <time datetime="2019-02">February 2019</time>
</div>

To build this, we first get the year-month indicator from the DOM with querySelector.

const yearMonthIndicator = document.querySelector('.datepicker__year-month')

Next, we need to get February from the date. We can get February if we create an array of months.

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

const month = date.getMonth()
const monthName = monthsInAYear[month]

Next, we want to get a 4-digit year. We can use getFullYear.

const year = date.getFullYear()

Once we know year and monthName, we can create the HTML.

yearMonthIndicator.innerHTML = `
  <time datetime="2019-02">${monthName} ${year}</time>
`

For the datetime attribute, we want to get a value that says 2019-02.

We know these:

  1. year: 2019
  2. month: 1

To get the correct month value, we can add 1 to month.

const oneIndexedMonth = month + 1

To get the 02, we can use a String method called padStart. This method takes in two arguments:

string.padStart(targetLength, padString)
  1. targetLength: The length you want your string to be. In this case, we want 2.
  2. padString: If the string is shorter than targetLength, we add the value given padString for each character.

Example:

'2'.padStart(2, '0') // 02
'1001'.padStart(8, 'x') // xxxx1001

To use padStart, we need to convert a Number into a String. We can convert a Number into a String with toString

Using padStart for our case:

const datetimeMonth = (month + 1).toString().padStart(2, '0')

The code for the year month indicator should be:

yearMonthIndicator.innerHTML = `
  <time datetime="${year}-${datetimeMonth}">${monthName} ${year}</time>
`
DOM view of the year-month indicator.

Date grid

We need to know tow things to build the date grid:

  1. What’s the first day of the month?
  2. What’s the number of days in the month?

First day of the month

To get the first day of the month, we can use setDate and set the date to 1. Then, we use getDay to know which day it is.

const firstDayOfMonth = new Date(date.setDate(1)).getDay()
console.log(firstDayOfMonth) // 5

Number of days in a month

JavaScript helps you calculate the correct date if you provide a value that’s outside of a normal calendar range.

So, if you set the day argument to 0, you’ll get the last day of the previous month.

const lastDayOfJanuary = new Date(2019, 1, 0)
console.log(lastDayOfJanuary) // Thu Jan 31 2019

To get the number of days in the previous month, we can use getDate.

const numDaysInJanuary = lastDayOfJanuary.getDate()
console.log(numDaysInJanuary) // 31

To get the number of days in the current month, we increase the month-number by one:

const year = date.getFullYear()
const month = date.getMonth()
const lastDayInMonth = new Date(year, month + 1, 0)
const daysInMonth = lastDayInMonth.getDate()
console.log(daysInMonth) // 28

Building the date grid

Each button in the day grid has the following HTML:

<button>
  <time datetime="2019-02-01">1</time>
</button>

We can use the daysInMonth value to create one button for each day of the month. The simplest way to do it is through a for loop.

const dategrid = document.querySelector('.datepicker__date-grid')

for (let day = 1; day <= daysInMonth; day++) {
  const button = document.createElement('button')
  button.innerHTML = `
    <time>${day}</time>
  `

  dategrid.appendChild(button)
}

We need to add the datetime attribute to the <time> element. Here, both month and day values need to be 2-digit.

We can use padStart like what we did before.

for (let day = 1; day <= daysInMonth; day++) {
  // ...
  const datetimeDay = day.toString().padStart(2, '0')

  button.innerHTML = `
    <time datetime="${year}-${datetimeMonth}-${datetimeDay}">${day}</time>
  `

  // ...
}

First day of the month

First, we want to remove the CSS that sets the first day of the month to Friday.

/* Remove this CSS */
.datepicker__dates button:first-child {
  grid-column: 6;
}

Next, we need to set grid-column to the correct value depending on the first day of the Month.

  1. grid-column for Sunday should be 1
  2. grid-column for Monday should be 2
  3. grid-column for Tuesday should be 3
  4. grid-column for Wednesday should be 4
  5. grid-column for Thursday should be 5
  6. grid-column for Friday should be 6
  7. grid-column for Saturday should be 7

One way to do this is set a CSS custom property on the date grid. We’ll use --firstDayOfMonth as the variable.

Here, We know JavaScript’s getDay function returns the following values:

  1. Sunday returns 0
  2. Monday returns 1
  3. Tuesday returns 2
  4. Wednesday returns 3
  5. Thursday returns 4
  6. Friday returns 5
  7. Saturday returns 6

This means --firstDayOfMonth should be getDay() + 1.

for (let day = 1; day <= daysInMonth; day++) {
  // ...
  if (day === 1) button.style.setProperty('--firstDayOfMonth', firstDayOfMonth + 1)
  // ...

  button.innerHTML = `
    <time datetime="${year}-${datetimeMonth}-${datetimeDay}">${day}</time>
  `

  // ...
}

Then we use --firstDayOfMonth in the CSS like this:

/* Add this CSS */
.datepicker__date-grid button:first-child {
  grid-column: var(--firstDayOfMonth);
}

Testing the calendar

You can test this calendar by changing the year and month values. It should look right no matter which year or month you pass into the initial date value.

  Three calendars. Use this to check with your output.