Today, I will show you how you can generate an input field that will enable users to select a date using JavaScript.
Our Date Input will take note of leap years and months that have either 31 or 30 days.
This means that :
- if you select a leap year, and you choose the month of February, the days will count up to 29
- If you choose the month of January, the days will count up to 31.
LET'S BEGIN
We will create a blank HTML file and add the Select fields; year, month, and day.
<!doctype html>
<html>
<head></head>
<body>
<select id="select_year" start="1900" end="2022">
</select>
<select id="select_month">
</select>
<select id="select_day">
</select>
</body>
</html>
We will leave the fields empty and use their respective IDs (Identifiers) to refer to them in JavaScript.
Generate Days
In order to generate the days, we will start at the index of 1 and increment the value to the maximum number of days in a calendar which is 31, then create an option tag and set both the value and text to our current index.
Generate Months
In order to generate the months, we will start at the index of 1 and increment the value to the maximum number of months in a calendar which is 12, then create an option tag and set both the value and text to our current index.
Generate Years
If you take a close look at the year Select Element, you will see the attributes start and end. These attributes will help us to generate the years from the specified start index to the end index.
document.addEventListener('DOMContentLoaded', function(){
const yearElem = document.querySelector('#select_year');
const dayElem = document.querySelector('#select_day');
const monthElem = document.querySelector('#select_month');
let days = 31; // maximum number of days
let months = 12; // maximum number of months
let yearStart = (yearElem.getAttribute("start")) ? yearElem.getAttribute("start") : 1900;
let yearEnd = (yearElem.getAttribute("end")) ? yearElem.getAttribute("end") : 2022;
let d = 1;
while(d <= days){
const opt = document.createElement("option");
opt.setAttribute("value", d);
opt.innerText = d;
dayElem.appendChild(opt);
++d;
}
let m = 1;
while(m <= months){
const opt = document.createElement("option");
opt.setAttribute("value", m);
opt.innerText = m;
monthElem.appendChild(opt);
++m;
}
while(yearStart <= yearEnd){
const opt = document.createElement("option");
opt.setAttribute("value", yearStart);
opt.innerText = yearStart;
yearElem.appendChild(opt);
++yearStart;
}
})
Regenerate Days
Now we need a function that will enable us to regenerate the days based on the length specified.
This means that if we choose the month of September (09), the days will be regenerated and will count up to 30 instead of 31 because there are 30 days in the month of September.
In other to achieve this, we will create an array inside our function, that will store the days from (1 - 31), then shorten the length of the array based on the length parameter specified.
function buildDays(length){
let daysArry = [];
let d = 1;
while(d <= 31){
daysArry.push(d);
++d;
}
//shorten the length
daysArry.length = ( daysArry.length - length );
//empty the days element
dayElem.innerHTML = '';
//loop through array, then create option tag and append to element
daysArry.forEach(d => {
const opt = document.createElement("option");
opt.setAttribute("value", d);
opt.innerText = d;
dayElem.appendChild(opt);
});
}
Now in other to make use of this function, we need to create another function that will check the month selected.
We will use a switch statement in this function, to check the month selected and set the length appropriately.
function checkMonth(month){
let len = 0;
//check month that has 30 days
switch(month){
//september
case "9" :
len = 1;
break;
//april
case "4" :
len = 1;
break;
//june
case "6" :
len = 1;
break;
//november
case "11" :
len = 1;
break;
default :
len = 0;
break;
}
//check if february is selected
if(month == 2){
checkLeapYear(yearElem.value);
}else{
buildDays(len);
}
}
For our last function, we need to check if the Year selected is a leap year.
Back in High School, my Math Teacher told me that if a year is divisible by 4, this means that such a year is a leap year.
For example, if you divide 2020 by 4, you will get an Integer (505). This means that the year 2020 was a leap year. But if you divide the year 2022 by 4, you will get a float (505.5). This means that the year 2022 is not a leap year.
So in our function, we will check if our division is an integer or a float using a Regular Expression, then rebuild our days appropriately.
//check leap year
function checkLeapYear(year){
let v = (year / 4) * 1;
//check if the result is an integer or a float using regExp
if(/^[0-9]+$/.test(v)){
(monthValue == 2) ?
buildDays(2) //29 days
: '';
}else{
(monthValue == 2) ?
buildDays(3) //28 days
: '';
}
}
Almost Done
Now we need to attach an Event Listener to our Month and Year select elements, in order to execute our functions based on the selected values.
We will also make sure of not executing the function twice when the same value is provided, by saving the value to a variable.
//Month Element Event Listener
var monthValue = 0
monthElem.addEventListener('click', function(){
if(monthValue !== this.value){
monthValue = this.value;
checkMonth(this.value); //run the check month function
}
});
//Year Element Event Listener
var yearValue = 0;
yearElem.addEventListener('click', function(){
yearValue = this.value;
checkLeapYear(this.value); //check for leap year
});
So if we should select a month, say September, the function will rebuild the days up to 30. And if we select the leap year, say 2020, the function will check if the month selected is February (2) and it will rebuild the days up to 29 because there're 29 days in the month of February on a leap year.
Testing our script
Copy and paste this code in a blank HTML page and try to select a leap year + February, a month that has 30 days in it.
document.addEventListener('DOMContentLoaded', function(){
const yearElem = document.querySelector('#select_year');
const dayElem = document.querySelector('#select_day');
const monthElem = document.querySelector('#select_month');
let days = 31; // maximum number of days
let months = 12; // maximum number of months
let yearStart = (yearElem.getAttribute("start")) ? yearElem.getAttribute("start") : 1900;
let yearEnd = (yearElem.getAttribute("end")) ? yearElem.getAttribute("end") : 2022;
let d = 1;
while(d <= days){
const opt = document.createElement("option");
opt.setAttribute("value", d);
opt.innerText = d;
dayElem.appendChild(opt);
++d;
}
let m = 1;
while(m <= months){
const opt = document.createElement("option");
opt.setAttribute("value", m);
opt.innerText = m;
monthElem.appendChild(opt);
++m;
}
while(yearStart <= yearEnd){
const opt = document.createElement("option");
opt.setAttribute("value", yearStart);
opt.innerText = yearStart;
yearElem.appendChild(opt);
++yearStart;
}
//build days
function buildDays(length){
let daysArry = [];
let d = 1;
while(d <= 31){
daysArry.push(d);
++d;
}
//shorten the length
daysArry.length = ( daysArry.length - length );
//empty the days element
dayElem.innerHTML = '';
//loop through array, then create option tag and append to element
daysArry.forEach(d => {
const opt = document.createElement("option");
opt.setAttribute("value", d);
opt.innerText = d;
dayElem.appendChild(opt);
});
}
//check selected month
function checkMonth(month){
let len = 0;
//check month that has 30 days
switch(month){
//september
case "9" :
len = 1;
break;
//april
case "4" :
len = 1;
break;
//june
case "6" :
len = 1;
break;
//november
case "11" :
len = 1;
break;
default :
len = 0;
break;
}
//check if february is selected
if(month == 2){
checkLeapYear(yearElem.value);
}else{
buildDays(len);
}
}
//check leap year
function checkLeapYear(year){
let v = (year / 4) * 1;
//check if the result is an integer or a float using regExp
if(/^[0-9]+$/.test(v)){
(monthValue == 2) ?
buildDays(2) //29 days
: '';
}else{
(monthValue == 2) ?
buildDays(3) //28 days
: '';
}
}
//Month Element Event Listener
var monthValue = 0
monthElem.addEventListener('click', function(){
if(monthValue !== this.value){
monthValue = this.value;
checkMonth(this.value); //run the check month function
}
});
//Year Element Event Listener
var yearValue = 0;
yearElem.addEventListener('click', function(){
yearValue = this.value;
checkLeapYear(this.value); //check for leap year
});
})
The Year 2020 (Leap Year) + Month of February
Month of January
Month of September
An Important Note
Most centurial years ( a period of 100 years ) are not leap years, even though they are divisible by 4.
Years such as 1700, 1800, and 1900 are not leap years but they are divisible by 4.
So this doesn’t agree with the fact that any year divisible by 4 must be a leap year.
According to this wonderful article, I got to realize that if a year is a centurial year ( a period of 100 years ), and is divisible by 4, it must also be divisible by 400 in order to qualify as a leap year.
Years 1700, 1800, 1900 are all divisible by 4 but are not divisible by 400, so they don’t qualify as leap years but the year 2000 is divisible by 4 and is also divisible by 400. This makes the year 2000 a leap year.
Let us update the function that checks for leap years. Here’s what we will do;
- First, we check if it is a centurial year ( a period of 100 years ).
If the year provided is divisible by 100 ( without a remainder ), then it is a centurial year.
Then we check if it is divisible by 400 and is also divisible by 4 ( without a remainder ), if it is, we return the boolean (true) which means that the year is a leap year.
- Second, we check if it is not a centurial year, but is divisible by 4.
If the year provided is divisible by 4 ( without a remainder ), we return the boolean (true) which means that the year is a leap year.
If none of the above is true, our function returns false, which means that the year provided is not a leap year.
function checkLeapYear(year){
let result = false;
//check for leap year
(function(){
//check for centurial year
if( (year % 100) == 0 ) {
//check if centurial year is a leap year
if( ((year % 400) == 0) && ((year % 4) == 0) ){
return ( result = true );
}
}else if( (year % 4) == 0){
//check if it is not a centurial year but is divisible by 4
return ( result = true );
}
return ( result = false );
})();
//check if the result is true ( leap year )
if(result){
(monthValue == 2) ?
buildDays(2) //29 days
: '';
}else{
(monthValue == 2) ?
buildDays(3) //28 days
: '';
}
}
All you have to do is to replace the function that checks for leap year with the function above, and the script will work 100% just fine.
Thank You!
Have you read my post on How you can validate your form fields easily, using octaValidate Library in JavaScript? If you haven't I recommend that you visit the post here.