CSS Challenges #3 | Calendar
Tags: css, challenge, easy, ui
Published: 2019-10-21 17:28:12

## Update

Due to some IRL stuff I'll be posting next challenge a week later. That also means that there will be more time for submissions. Sorry for the inconvenience.

----

It's already the third week, damn is time flying or what. Here are submissions for last week:
{% link https://dev.to/mikister2012/css-challenges-results-2-tooltips-1ggf %}


## What is this

If you're new, hello there! It's good that you want to improve your skills with css. This is a series aimed at helping you do just that by challenging you to recreate a suggested feature or element. I'll only be posting challenges that will be of use when building stuff. So no code golf or obscure stuff.

This is a weekly challenge, so every Monday there will be a new thing to build. Also every Monday I'll summarise all the submissions to last week's challenge.

Post links to your solutions (codepens, jsfiddles, whatever your prefer) in the comments if you solved the challenge.


## Calendar

This time the challenge is to make a calendar. You can do the logic in pure js, React, Vue, etc.

And for anyone who's not experienced with javascript or doesn't want to deal with it, here's I've provided some pure js code that does everything, so you can focus on the design. Just follow the design doc bellow.

## Requirements

Calendar should contain:

* text displaying the month you're looking at (and year if it's not the current one)
* buttons for switching the selected month
* button to return to selected month
* initials for each day in month
* days

## The JS, so you don't have to do it

```js
var calendarNode = document.querySelector("#calendar");

var currDate = new Date();
var currYear = currDate.getFullYear();
var currMonth = currDate.getMonth() + 1;

var selectedYear = currYear;
var selectedMonth = currMonth;
var selectedMonthName = getMonthName(selectedYear, selectedMonth);
var selectedMonthDays = getDayCount(selectedYear, selectedMonth);

renderDOM(selectedYear, selectedMonth);

function getMonthName (year, month) {
    let selectedDate = new Date(year, month-1, 1);
    return selectedDate.toLocaleString('default', { month: 'long' });
}

function getMonthText () {
    if (selectedYear === currYear)
        return selectedMonthName;
    else
        return selectedMonthName + ", " + selectedYear;
}

function getDayName (year, month, day) {
    let selectedDate = new Date(year, month-1, day);
    return selectedDate.toLocaleDateString('en-US',{weekday: 'long'});
}

function getDayCount (year, month) {
    return 32 - new Date(year, month-1, 32).getDate();
}

function getDaysArray () {
    let emptyFieldsCount = 0;
    let emptyFields = [];
    let days = [];

    switch(getDayName(selectedYear, selectedMonth, 1))
    {
        case "Tuesday":
            emptyFieldsCount = 1;
            break;
        case "Wednesday":
            emptyFieldsCount = 2;
            break;
        case "Thursday":
            emptyFieldsCount = 3;
            break;
        case "Friday":
            emptyFieldsCount = 4;
            break;
        case "Saturday":
            emptyFieldsCount = 5;
            break;
        case "Sunday":
            emptyFieldsCount = 6;
            break;
    }
  
    emptyFields = Array(emptyFieldsCount).fill("");
    days = Array.from(Array(selectedMonthDays + 1).keys());
    days.splice(0, 1);
    
    return emptyFields.concat(days);
}

function renderDOM (year, month) {
  let newCalendarNode = document.createElement("div");
  newCalendarNode.id = "calendar";
  newCalendarNode.className = "calendar";
  
  let dateText = document.createElement("div");
  dateText.append(getMonthText());
  dateText.className = "date-text";
  newCalendarNode.append(dateText);
  
  let leftArrow = document.createElement("div");
  leftArrow.append("«");
  leftArrow.className = "button";
  leftArrow.addEventListener("click", goToPrevMonth);
  newCalendarNode.append(leftArrow);
  
  let curr = document.createElement("div");
  curr.append("📅");
  curr.className = "button";
  curr.addEventListener("click", goToCurrDate);
  newCalendarNode.append(curr);
  
  let rightArrow = document.createElement("div");
  rightArrow.append("»");
  rightArrow.className = "button";
  rightArrow.addEventListener("click", goToNextMonth);
  newCalendarNode.append(rightArrow);
  
  let dayNames = ["M", "T", "W", "T", "F", "S", "S"];
  
  dayNames.forEach((cellText) => {
    let cellNode = document.createElement("div");
    cellNode.className = "cell cell--unselectable";
    cellNode.append(cellText);
    newCalendarNode.append(cellNode);
  });
  
  let days = getDaysArray(year, month);
  
  days.forEach((cellText) => {
    let cellNode = document.createElement("div");
    cellNode.className = "cell";
    cellNode.append(cellText);
    newCalendarNode.append(cellNode);
  });
  
  calendarNode.replaceWith(newCalendarNode);
  calendarNode = document.querySelector("#calendar");
}

function goToPrevMonth () {
    selectedMonth--;
    if (selectedMonth === 0) {
        selectedMonth = 12;
        selectedYear--;
    }
    selectedMonthDays = getDayCount(selectedYear, selectedMonth);
    selectedMonthName = getMonthName(selectedYear, selectedMonth);
  
    renderDOM(selectedYear, selectedMonth);
}

function goToNextMonth () {
    selectedMonth++;
    if (selectedMonth === 13) {
        selectedMonth = 0;
        selectedYear++;
    }
    selectedMonthDays = getDayCount(selectedYear, selectedMonth);
    selectedMonthName = getMonthName(selectedYear, selectedMonth);
  
    renderDOM(selectedYear, selectedMonth);
}

function goToCurrDate () {
    selectedYear = currYear;
    selectedMonth = currMonth;

    selectedMonthDays = getDayCount(selectedYear, selectedMonth);
    selectedMonthName = getMonthName(selectedYear, selectedMonth);
  
    renderDOM(selectedYear, selectedMonth);
}
```

### Design doc

* for the HTML just add `<div id="calendar" class="calendar"></div>`
* the `renderDOM` function will add the following to your calendar div:
  * text, telling the current month (with `class="date-text"`)
  * buttons for going to previous, current and next month (with `class="button"`)
  * initials for each day of month (with `class="cell cell--unselectable"`)
  * numbers for each day in month and appropriate amount of empty cells (with `class="cell"`)


## Read More

For those who use provided JS:
* [MDN - grid-auto-flow](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
* [MDN - grid-column](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column)

For those wanting to do everything:
* [MDN - Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
* [MDN - Date.prototype.toLocaleString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString)
* [MDN - Array.prototype.fill()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill)

----

That's it for this week, have fun.