Sunday 18 July 2021

Web Tutorial: The JS Datepicker (Part 3/4)

Fun times! It's time to implement some click events.

Remember we could change the month display just by selecting the value of monthPicker? Well, let's expand on that a bit. We can work on the btnMonthDec and btnMonthInc elements in the initialize() method. Add a click event on both btnMonthDec and btnMonthInc. The content of the callback will be similar to monthPicker's change event... except that instead of passing in null for the month, you pass in the month number before and after the current month, for btnMonthDec and btnMonthInc respectively. Don't worry about showDate(); we'll be on that in due course.
var btnMonthDec = document.createElement("div");
btnMonthDec.classList.add("button");
btnMonthDec.classList.add("spaceleft");
btnMonthDec.innerHTML = "◀";
btnMonthDec.onclick = function() { calendar.setCalendarDate(id, null, calendar.selectedDate.getMonth() + 1 - 1, null);calendar.showDate(); };
bottom.appendChild(btnMonthDec);

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(id, null, null, null);calendar.showDate(); };
bottom.appendChild(monthPicker);

var btnMonthInc = document.createElement("div");
btnMonthInc.classList.add("button");
btnMonthInc.classList.add("spaceleft");
btnMonthInc.innerHTML = "&#9654;";
btnMonthInc.onclick = function() { calendar.setCalendarDate(id, null, calendar.selectedDate.getMonth() + 1 + 1, null);calendar.showDate(); };
bottom.appendChild(btnMonthInc);


Now you can see, that when you click on these buttons, the month goes back and forth!


But if you get to December and click again, it goes blank! Let's fix that.


The setCalendarDate() method is where we fix this. Add an Else block after checking if month is null. If month is 13, reset monthVal to January (1) and increment yearVal. If month is 0, reset monthVal to December (12) and decrement yearVal.
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;
    }
    else
    {
        if (month == 13)
        {
            monthVal = 1;
            yearVal++;
        }

        if (month == 0)
        {
            monthVal = 12;
            yearVal--;
        }
    }


So now, if you're at December 2021 and you click to increment the month, it goes to January and the year changes to 2022.


If you're at January 2021 and you click to decrement the month, it goes to December and the year changes to 2020.


Next, we will write the code to handle changing the year. This part will be relatively easy because we're just leveraging off code we've already written. So in the initialize() method, add these lines. Note that in these cases, we pass in id and nulls, but the year portion of selectedDate as the argument for year. If you want to decrement by 10, you have to subtract 10 from the argument. If you want to increment by 1, you have to add 1 to the argument. And so on.
var btnYearDecTen = document.createElement("div");
btnYearDecTen.classList.add("button");
btnYearDecTen.classList.add("spaceleft_large");
btnYearDecTen.innerHTML = "&#9665;";
btnYearDecTen.onclick = function() { calendar.setCalendarDate(id, null, null, calendar.selectedDate.getFullYear() - 10);calendar.showDate(); };
bottom.appendChild(btnYearDecTen);

var btnYearDec = document.createElement("div");
btnYearDec.classList.add("button");
btnYearDec.classList.add("spaceleft");
btnYearDec.innerHTML = "&#9664;";
btnYearDec.onclick = function() { calendar.setCalendarDate(id, null, null, calendar.selectedDate.getFullYear() - 1);calendar.showDate(); };
bottom.appendChild(btnYearDec);

var lblYear = document.createElement("div");
lblYear.classList.add("lblYear");
lblYear.classList.add("spaceleft");
lblYear.id = "lblyear_" + id;
lblYear.innerHTML = this.selectedDate.getFullYear();
bottom.appendChild(lblYear);

var btnYearInc = document.createElement("div");
btnYearInc.classList.add("button");
btnYearInc.classList.add("spaceleft");
btnYearInc.innerHTML = "&#9654;";
btnYearInc.onclick = function() { calendar.setCalendarDate(id, null, null, calendar.selectedDate.getFullYear() + 1);calendar.showDate(); };
bottom.appendChild(btnYearInc);

var btnYearIncTen = document.createElement("div");
btnYearIncTen.classList.add("button");
btnYearIncTen.classList.add("spaceleft");
btnYearIncTen.innerHTML = "&#9655;";
btnYearIncTen.onclick = function() { calendar.setCalendarDate(id, null, null, calendar.selectedDate.getFullYear() + 10);calendar.showDate(); };
bottom.appendChild(btnYearIncTen);


Now try clicking on the various year buttons. You should be able to increment and decrement the year, by 1 or 10! And the dates in daysContainer should change accordingly!


Great, now it's time for date selection. To do this, we first need to add an element into the entire setup. This should be put near the end of the initialize() method. It's supposed to be a hidden element, but for now, we will make it a textbox so that we can see what's going on. This is the most pertinent part of the datepicker because it's where the value is actually stored.
    var daysContainer = document.createElement("div");
    daysContainer.classList.add("daysContainer");
    daysContainer.id = "dayscontainer_" + id;
    bottom.appendChild(daysContainer);

    var hiddenTextbox = document.createElement("input");
    hiddenTextbox.type = "text";
    hiddenTextbox.id = "hidtxt_" + id;
    hiddenTextbox.name = id;
    bottom.appendChild(hiddenTextbox)
;

    this.setProperties(id);
    this.setCalendarDate(id, this.selectedDate.getDate(), this.selectedDate.getMonth() + 1, this.selectedDate.getFullYear());
},


There you see the textbox we just added!


In setProperties(), we need to do the same for hiddenTextBox.
setProperties: function(id)
{
    this.container = document.getElementById(id);
    this.textbox = document.getElementById("txt_" + id);
    this.hiddenTextbox = document.getElementById("hidtxt_" + id);
    this.monthPicker = document.getElementById("monthpicker_" + id);
    this.lblYear = document.getElementById("lblyear_" + id);
    this.daysContainer = document.getElementById("dayscontainer_" + id);
},


At the end of setCalendarDate(), add this line to populate the textbox. Note that we format selectedDate to a value that can be sent to the server.
    this.lblYear.innerHTML = yearVal;
    this.hiddenTextbox.value = yearVal + "-" + (this.selectedDate.getMonth() + 1) + "-" + this.selectedDate.getDate();

    this.setMonthDisplay(id, monthVal, yearVal);
},


Now if you refresh, you will see the date in the textbox. This is the value that is going to be sent to the server. If you change the value on monthPicker, the value in hiddenTextbox changes as well.


You'll notice that we have been calling showDate() in some places, and even have the method created. Well, now we are about to fill that in. This time, we fill in textbox, but with a user-friendly prettified date.
showDate: function()
{
    this.textbox.value = this.selectedDate.getDate() + " " + this.months[this.selectedDate.getMonth()] + " " + this.selectedDate.getFullYear();
},


Now when you change the value of monthPicker, you can see the date and the top change!


For date selection, go to the generateDateBox() method. Before returning dayBox, we need to add a click event. When the user clicks on any one of these dates, the setCalendarDate() method is called. Remember we set the data of day, month and year earlier in the element? Well, now we will use these as arguments in the callback.
    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();
    dayBox.onclick = function(e) { calendar.setCalendarDate(id, e.target.dataset["day"], e.target.dataset["month"], e.target.dataset["year"]); calendar.showDate();};

    return dayBox;
}


So now if you go to the July display and click on, say, 29, the dates in the textboxes change!


Now for one last thing. The widget controls have been visible up to now. We need to be able to hide and un-hide them. At the beginning of the initialize() method add this line so that container is styled using hide by default. So now if you refresh, the controls should no longer be visible.
initialize: function(id)
{
    this.selectedDate = new Date();
    this.container = document.getElementById(id);
    this.container.classList.add("calendarContainer");
    this.container.classList.add("hide");


And then, in the same method, further down the line to where btnDisplay is defined, add this line to call the display() method.
var btnDisplay = document.createElement("div");
btnDisplay.classList.add("button");
btnDisplay.classList.add("spaceleft");
btnDisplay.id = "btndisplay_" + id;
btnDisplay.innerHTML = "&#9660;";
btnDisplay.onclick = function() { calendar.display(id); };
top.appendChild(btnDisplay);


We'll define this method next.
setProperties: function(id)
{
    this.container = document.getElementById(id);
    this.textbox = document.getElementById("txt_" + id);
    this.hiddenTextbox = document.getElementById("hidtxt_" + id);
    this.monthPicker = document.getElementById("monthpicker_" + id);
    this.lblYear = document.getElementById("lblyear_" + id);
    this.daysContainer = document.getElementById("dayscontainer_" + id);
},
display: function(id)
{

},

setMonthDisplay: function(id, month, year)
{


So when display() is called, we call setProperties(). The variable show is used to check if container is styled using hide. So if container is invisible, show is true, false otherwise.
display: function(id)
{
    this.setProperties(id);
    var show = this.container.classList.contains("hide");

},


So if container is invisible, it means show is true, and we want the button click to show the controls. In that case, we want to remove the CSS class hide and add the CSS class show.
display: function(id)
{
    this.setProperties(id);
    var show = this.container.classList.contains("hide");

    if (show)
    {
        this.container.classList.remove("hide");
        this.container.classList.add("show");
    }
    else
    {

    }

},


If show is false, we remove show and add hide.
display: function(id)
{
    this.setProperties(id);
    var show = this.container.classList.contains("hide");

    if (show)
    {
        this.container.classList.remove("hide");
        this.container.classList.add("show");
    }
    else
    {
        this.container.classList.remove("show");
        this.container.classList.add("hide");

    }
},


Now refresh. When you click the btnDisplay button, it toggles the display on and off!

Next

Cleaning up. There's some more stuff we need to take care of before this is complete.

No comments:

Post a Comment