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:
Article No Longer Available
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);
}
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"
)
- text, telling the current month (with
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)
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 ;)
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 thegrid-areas
set.But I liked how it turned out.
Also here's two link I think you might consider useful:
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!
Hi Milan! My entry:
jsfiddle.net/alanhchoi/oa7yfgLu/78/
I used toLocaleString() so that people can see the calendar in their languages.