DEV Community

Cover image for CSS Challenges #3 | Calendar
Milan Radojević
Milan Radojević

Posted on • Updated on

CSS Challenges #3 | Calendar

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:

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

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);
}
Enter fullscreen mode Exit fullscreen mode

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:

For those wanting to do everything:


That's it for this week, have fun.

Top comments (4)

Collapse
 
davide profile image
Davide Scarioni

My entry:
codepen.io/Scario/pen/bGGWVWo

I use your JS, but I fix a bug that I found when I switch from december to january and come back and add currDay to add a CSS class for the current day.

Not my best code, but I like the result ;)

Collapse
 
mikister profile image
Milan Radojević

Looking great love the fresh design.

One thing to note is that css grid will place the elements for you, and if you want to make certain elements take up more than one column you can use grid-column property. So you don't need the grid-areas set.

But I liked how it turned out.

Also here's two link I think you might consider useful:

Collapse
 
davide profile image
Davide Scarioni

Thank you! :)
I use grid-areas only because I think it is neater when I use the grid inspector of Firefox, but I understand your point.

I really appreciate your link, I never use both!

Collapse
 
alanhchoi profile image
Alan Choi

Hi Milan! My entry:
jsfiddle.net/alanhchoi/oa7yfgLu/78/

I used toLocaleString() so that people can see the calendar in their languages.