Welcome back. In this part, we will be displaying the dates of the current month.
Let's create
daysContainer. It's the element where we will place all the dates. It will be styled using the CSS class
daysContainer, and have an id based on "cal1". Append this to
bottom.
var btnYearIncTen = document.createElement("div");
btnYearIncTen.classList.add("button");
btnYearIncTen.classList.add("spaceleft");
btnYearIncTen.innerHTML = "▷";
bottom.appendChild(btnYearIncTen);
var daysContainer = document.createElement("div");
daysContainer.classList.add("daysContainer");
daysContainer.id = "dayscontainer_" + id;
bottom.appendChild(daysContainer);
This is the styling for
daysContainer. There's nothing visible about it, but it will keep all the dates in line when we start adding them.
.calendarContainer .lblYear
{
width: 80px;
height: 24px;
text-align: center;
float: left;
font-size: 1.6em;
}
.daysContainer
{
width: 100%;
height: 150px;
margin-top: 10px;
text-align: center;
float: left;
}
.spaceleft
{
margin-left: 2px;
}
Now, in the
initialize() method, call the
setProperties() method, passing in
id as an argument.
var daysContainer = document.createElement("div");
daysContainer.classList.add("daysContainer");
daysContainer.id = "dayscontainer_" + id;
bottom.appendChild(daysContainer);
this.setProperties(id);
Then add the
setProperties() method.
this.setProperties(id);
},
setProperties: function(id)
{
},
In here, we will set all the properties of the calendar object. Remember we created ids for all these elements, based on "cal1"? Well, the getElementById() method here comes in handy as we assign all these elements to properties within the calendar object, for easy reference.
this.setProperties(id);
},
setProperties: function(id)
{
this.container = document.getElementById(id);
this.textbox = document.getElementById("txt_" + id);
this.monthPicker = document.getElementById("monthpicker_" + id);
this.lblYear = document.getElementById("lblyear_" + id);
this.daysContainer = document.getElementById("dayscontainer_" + id);
},
Now, in the
initialize() method where we first created
monthPicker, add an event handler. Whenever the value in there is changed, the
setCalendarDate() and
showDate() methods are called.
var monthPicker = document.createElement("select");
monthPicker.classList.add("spaceleft");
monthPicker.id = "monthpicker_" + id;
for (var i = 0; i < this.months.length; i++)
{
var option = document.createElement("option");
option.value = i + 1;
option.innerHTML = this.months[i];
monthPicker.appendChild(option);
}
monthPicker.onchange = function() { calendar.setCalendarDate();calendar.showDate(); };
bottom.appendChild(monthPicker);
setCalendarDates() require four arguments to be passed in. Pass in
id, then
null for the others.
monthPicker.onchange = function() { calendar.setCalendarDate(id, null, null, null);calendar.showDate(); };
Add the
setCalendarDate() and
showDate() methods. We will be working on
setCalendarDate() only, and leaving
showDate() alone for now.
setProperties: function(id)
{
this.container = document.getElementById(id);
this.textbox = document.getElementById("txt_" + id);
this.monthPicker = document.getElementById("monthpicker_" + id);
this.lblYear = document.getElementById("lblyear_" + id);
this.daysContainer = document.getElementById("dayscontainer_" + id);
},
setCalendarDate: function(id, day, month, year)
{
},
showDate: function()
{
},
In
setCalendarDate(), we begin by declaring the variables
monthVal,
yearVal and
dayVal. Then we set them to the values of
month,
year and
day respectively.
setCalendarDate: function(id, day, month, year)
{
var monthVal = month;
var yearVal = year;
var dayVal = day;
},
Since we passed in
null values, we are going to handle that. If
day is
null,
dayVal will be the date of the
selectedDate property, which is set to the current date, by default. Same for
year.
setCalendarDate: function(id, day, month, year)
{
var monthVal = month;
var yearVal = year;
var dayVal = day;
if (day == null)
{
dayVal = this.selectedDate.getDate();
}
if (year == null)
{
yearVal = this.selectedDate.getFullYear();
}
},
But for
month,
monthVal will be set to the current value of
monthPicker.
setCalendarDate: function(id, day, month, year)
{
var monthVal = month;
var yearVal = year;
var dayVal = day;
if (day == null)
{
dayVal = this.selectedDate.getDate();
}
if (year == null)
{
yearVal = this.selectedDate.getFullYear();
}
if (month == null)
{
monthVal = this.monthPicker.value;
}
},
At this point,
selectedDate should be reset using
yearVal,
monthVal (deduct 1 because in JavaScript, months start from 0) and
dayVal. And
lblYear should display the value of
yearVal. This may seem really superfluous now, but trust me, it will be useful later.
if (month == null)
{
monthVal = this.monthPicker.value;
}
this.selectedDate = new Date(yearVal, monthVal - 1, dayVal);
this.lblYear.innerHTML = yearVal;
After all this, call
setMonthDisplay(), passing in
id,
monthVal and
yearVal. This is supposed to display all the dates for the month denoted by
monthVal.
if (month == null)
{
monthVal = this.monthPicker.value;
}
this.selectedDate = new Date(yearVal, monthVal - 1, dayVal);
this.lblYear.innerHTML = yearVal;
this.setMonthDisplay(id, monthVal, yearVal);
},
Now let's add
setMonthDisplay() as a method. It accepts three parameters -
id,
month and
year.
setProperties: function(id)
{
this.container = document.getElementById(id);
this.textbox = document.getElementById("txt_" + id);
this.monthPicker = document.getElementById("monthpicker_" + id);
this.lblYear = document.getElementById("lblyear_" + id);
this.daysContainer = document.getElementById("dayscontainer_" + id);
},
setMonthDisplay: function(id, month, year)
{
},
setCalendarDate: function(id, day, month, year)
{
We start by setting
monthPicker's value to
month. This will seem counter-intuitive considering we arrived here by setting
monthPicker's value in the first place. But here, we are actually closing a loop by ensuring that this method and
monthPicker will have the same month value even if
setMonthDisplay() was called through some way other than by activating
monthPicker.
setMonthDisplay: function(id, month, year)
{
this.monthPicker.value = month;
},
And then we clear
daysContainer of content.
setMonthDisplay: function(id, month, year)
{
this.monthPicker.value = month;
this.daysContainer.innerHTML = "";
},
Now, iterate through the
days array. We're going to create headers for the table.
setMonthDisplay: function(id, month, year)
{
this.monthPicker.value = month;
this.daysContainer.innerHTML = "";
for (var i = 0; i < this.days.length; i++)
{
}
},
In the
For loop, create div elements and ensure that each one is styled using
day,
header and
spaceleft. And append to
daysContainer.
setMonthDisplay: function(id, month, year)
{
this.monthPicker.value = month;
this.daysContainer.innerHTML = "";
for (var i = 0; i < this.days.length; i++)
{
var dayHeader = document.createElement("div");
dayHeader.classList.add("day");
dayHeader.classList.add("header");
dayHeader.classList.add("spaceleft");
this.daysContainer.append(dayHeader);
}
},
But before that step, check if the current value is a Saturday (6) or Sunday (0). If so, make sure the div is styled using
weekend as well.
setMonthDisplay: function(id, month, year)
{
this.monthPicker.value = month;
this.daysContainer.innerHTML = "";
for (var i = 0; i < this.days.length; i++)
{
var dayHeader = document.createElement("div");
dayHeader.classList.add("day");
dayHeader.classList.add("header");
dayHeader.classList.add("spaceleft");
if (i == 0 || i == 6) dayHeader.classList.add("weekend");
this.daysContainer.append(dayHeader);
}
},
Fill in the div using the value of the current element of
days.
setMonthDisplay: function(id, month, year)
{
this.monthPicker.value = month;
this.daysContainer.innerHTML = "";
for (var i = 0; i < this.days.length; i++)
{
var dayHeader = document.createElement("div");
dayHeader.classList.add("day");
dayHeader.classList.add("header");
dayHeader.classList.add("spaceleft");
if (i == 0 || i == 6) dayHeader.classList.add("weekend");
dayHeader.innerHTML = this.days[i];
this.daysContainer.append(dayHeader);
}
},
It's a good start, but we will need to write some CSS.
Here's the styling for
day,
header and
weekend. We already have styling for
spaceleft. The styling for
day pertains to width and height, text alignment and most importantly, set the
float property to
left.
header merely sets the text to bold. And
weekend sets the font color to
red.
.daysContainer
{
width: 100%;
height: 150px;
margin-top: 10px;
text-align: center;
float: left;
}
.daysContainer .day
{
width: 47px;
height: 20px;
text-align: center;
float: left;
}
.daysContainer .header
{
font-weight:bold;
}
.daysContainer .weekend
{
color: rgba(255, 0, 0, 1);
}
.spaceleft
{
margin-left: 2px;
}
Now as you can see, it all appears in a straight line (due to us setting the
float property to
left), the text is boldened, and the weekends appear in
red.
Now we start filling in dates. We define
firstDate as the first date of the current month defined by month (minus 1, for the same reasons mentioned earlier).
firstDay is the first weekday that
firstDate is on. (Monday, Tuesday, etc)
for (var i = 0; i < this.days.length; i++)
{
var dayHeader = document.createElement("div");
dayHeader.classList.add("day");
dayHeader.classList.add("header");
dayHeader.classList.add("spaceleft");
if (i == 0 || i == 6) dayHeader.classList.add("weekend");
dayHeader.innerHTML = this.days[i];
this.daysContainer.append(dayHeader);
}
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
Then we define
tempDate. We set this to the same value as
firstDate.
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
var tempDate = new Date(year, month - 1, 1);
Now we define a
While loop, which will run as long as
tempDate has the same month as
firstDate. Within the loop, increase
tempDate by one day. So this will run until
tempDate reaches the end of the current month defined by
month!
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
var tempDate = new Date(year, month - 1, 1);
while (tempDate.getMonth() == firstDate.getMonth())
{
tempDate.setDate(tempDate.getDate() + 1);
}
And we use this
While loop to append something to
daysContainer...
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
var tempDate = new Date(year, month - 1, 1);
while (tempDate.getMonth() == firstDate.getMonth())
{
this.daysContainer.appendChild();
tempDate.setDate(tempDate.getDate() + 1);
}
And what this thing is, is the element returned by the
generateDayBox() method. In this method, we pass in
id,
tempDate, and
true as arguments.
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
var tempDate = new Date(year, month - 1, 1);
while (tempDate.getMonth() == firstDate.getMonth())
{
this.daysContainer.appendChild(calendar.generateDayBox(id, tempDate, true));
tempDate.setDate(tempDate.getDate() + 1);
}
Now, let's define the
generateDayBox() method. In here, we create a new div element,
dayBox, and eventually return it.
showDate: function()
{
},
generateDayBox: function(id, date, isCurrentMonth)
{
var dayBox = document.createElement("div");
return dayBox;
}
And we fill in
dayBox with the value of
date.
generateDayBox: function(id, date, isCurrentMonth)
{
var dayBox = document.createElement("div");
dayBox.innerHTML = date.getDate();
return dayBox;
}
If
day is a weekend, add the CSS class
weekend.
generateDayBox: function(id, date, isCurrentMonth)
{
var dayBox = document.createElement("div");
if (date.getDay() == 0 || date.getDay() == 6) dayBox.classList.add("weekend");
dayBox.innerHTML = date.getDate();
return dayBox;
}
And add in more CSS classes -
date and
spaceleft.
generateDayBox: function(id, date, isCurrentMonth)
{
var dayBox = document.createElement("div");
dayBox.classList.add("date");
dayBox.classList.add("spaceleft");
if (date.getDay() == 0 || date.getDay() == 6) dayBox.classList.add("weekend");
dayBox.innerHTML = date.getDate();
return dayBox;
}
And here, we set the data of the element -
day,
month and
year. This will be important in the next part of this tutorial.
isCurrentMonth is going to be important later on in this part.
generateDayBox: function(id, date, isCurrentMonth)
{
var dayBox = document.createElement("div");
dayBox.classList.add("date");
dayBox.classList.add("spaceleft");
dayBox.dataset.day = date.getDate();
dayBox.dataset.month = date.getMonth() + 1;
dayBox.dataset.year = date.getFullYear();
if (date.getDay() == 0 || date.getDay() == 6) dayBox.classList.add("weekend");
dayBox.innerHTML = date.getDate();
return dayBox;
}
You see when we select, for example, February, the numbers go from 1 to 28, but it's all in a mess. Time to do some styling!
If we do this,
date will take on whatever
day is styled by!
.daysContainer .day, .daysContainer .date
{
width: 47px;
height: 20px;
text-align: center;
float: left;
}
Since
weekend has already been styled as well, the colors and layout look better now. But 1st February 2021 is not on a Sunday - it's on a Monday. We will need to fix that.
In the
setMonthDisplay() method, before displaying the current month's days, we first want to display the tail end of the previous month. Set an
If block and check if the first day does not fall on a Sunday.
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
if (firstDay > 0)
{
}
var tempDate = new Date(year, month - 1, 1);
Within that block, declare
tempDate and set it to the same value as
firstDate. Then set it one day earlier.
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
if (firstDay > 0)
{
var tempDate = new Date(year, month - 1, 1);
tempDate.setDate(firstDate.getDate() - firstDay - 1);
}
var tempDate = new Date(year, month - 1, 1);
Now, we just want to fill in all those days from Sunday to the day preceding the first day. So if the first day of the month falls on, say, Wednesday, we want to fill in the days for Sunday, Monday and Tuesday. For that, create a
For loop that will go from Sunday (0) to the day preceding the first day.
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
if (firstDay > 0)
{
var tempDate = new Date(year, month - 1, 1);
tempDate.setDate(firstDate.getDate() - firstDay - 1);
for (var i = 0; i < firstDay; i++)
{
}
}
var tempDate = new Date(year, month - 1, 1);
And in this loop, we keep increasing
tempDate by one day. And append to
daysContainer the same way we did for the days of the current month! But note that the last argument is
false, because it's not the current month.
var firstDate = new Date(year, month - 1, 1);
var firstDay = firstDate.getDay();
if (firstDay > 0)
{
var tempDate = new Date(year, month - 1, 1);
tempDate.setDate(firstDate.getDate() - firstDay - 1);
for (var i = 0; i < firstDay; i++)
{
tempDate.setDate(tempDate.getDate() + 1);
this.daysContainer.appendChild(calendar.generateDayBox(id, tempDate, false));
}
}
var tempDate = new Date(year, month - 1, 1);
Now when you select, for example, April, you will see the days of the week from Sunday to the first day of April (which is Thursday) have been filled in!
And the other side of the coin is, we need to fill in the rest of the dates from the end of the current month, until Saturday! So let's do this in the
setMonthDisplay() method. After displaying the days of the current month, check if the day after the last day of the current month (which is actually the value of
tempDate at this point) falls on any day other than Sunday.
while (tempDate.getMonth() == firstDate.getMonth())
{
this.daysContainer.appendChild(calendar.generateDayBox(id, tempDate, true));
tempDate.setDate(tempDate.getDate() + 1);
}
if (tempDate.getDay() != 0)
{
}
},
When that happens, we want to append at least one more day to
daysContainer. Again, we have to run
generateDayBox(), and the last argument has to be
false.
if (tempDate.getDay() != 0)
{
this.daysContainer.appendChild(calendar.generateDayBox(id, tempDate, false));
}
And the next piece of code runs for as long as
tempDate is earlier than Saturday (i.e, we want to fill in the rest of the days until Saturday.) and keep adding one day to
tempDate and appending a day to
daysContainer.
if (tempDate.getDay() != 0)
{
this.daysContainer.appendChild(calendar.generateDayBox(id, tempDate, false));
while (tempDate.getDay() < 6) {
tempDate.setDate(tempDate.getDate() + 1);
this.daysContainer.appendChild(calendar.generateDayBox(id, tempDate, false));
}
}
So now you see the rest of the dates for the next month are filled in!
OK, so this is where
isCurrentMonth comes in. If it's
false, add the CSS class
notCurrentMonth.
generateDayBox: function(id, date, isCurrentMonth)
{
var dayBox = document.createElement("div");
dayBox.classList.add("date");
if (!isCurrentMonth) dayBox.classList.add("notCurrentMonth");
dayBox.classList.add("spaceleft");
dayBox.dataset.day = date.getDate();
dayBox.dataset.month = date.getMonth() + 1;
dayBox.dataset.year = date.getFullYear();
The CSS we write for this is first, for
date. Remember we previously already styled
date? Well, this is an override, or an add-on. We add a little padding on top, and change the color slightly. Most importantly, we ensure that the text is bold, and that when moused over, the cursor is a pointer. Because we'll be clicking on these suckers. And then
notCurrentMonth un-bolds the text.
.daysContainer .header
{
font-weight:bold;
}
.daysContainer .date
{
padding-top: 3px;
color: rgba(100, 100, 100, 1);
font-weight: bold;
cursor: pointer;
}
.daysContainer .notCurrentMonth
{
font-weight: normal;
}
.daysContainer .weekend
{
color: rgba(255, 0, 0, 1);
}
And now you can see all the displayed dates of the currently selected month are in bold, and those for the previous and next month are not.
Now, at the end of
initalize(), let's make a call to
setCalendatDate().
this.setProperties(id);
this.setCalendarDate(id, this.selectedDate.getDate(), this.selectedDate.getMonth() + 1, this.selectedDate.getFullYear());
},
It's currently June as I write this, so once you refresh the page, as soon as
initialize() is run, the displayed date shows the current month!
Still here?
You hung in there! I'm impressed. So far this is all display. Fortunately, most of the calculation is done.
Next
Click events and date selection.