Easily Generate Custom Date Field Input with JavaScript
Tags: javascript, webdev, beginners, tutorial
Published: 2022-06-03 00:10:52

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.

```html
<!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.


```javascript
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;
    }
})
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642688106487/fVC4Tsbl2.png)

### 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.

```javascript
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.

```javascript
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.

```javascript
//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.

```javascript
//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.

```javascript
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

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642691857714/0OFiRHek1.png)

### Month of January

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642691978607/gZc6fKMWZ.png)

### Month of September

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642692038310/vOmKCRbDr.png)

## 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](https://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm), 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.

```javascript
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.

You've reached the end of my article.

## EXTRA

I recently launched a JavaScript package that validates your HTML forms using validation rules, regular expressions and form input attributes.

I will appreciate if you spared a few seconds to check it out.

{% embed https://www.producthunt.com/posts/octavalidate %}

Thank You

Image credit: vecteezy.com