4
\$\begingroup\$

I'm still new in javascript and learning. I want to create a date picker using html and javascript. Below is what I have tried and it's working, so my question is "how can I do it better?" My idea is to create a custom tag in HTML and allow multiple of these tags. The javascript part will then handle the build and functionality of all the custom tags. Like I said it is working, I just want to get some feedback to help making it better.

I have done everything, The HTML and javascript and both are working. See below my code:

let currentDate = new Date();

let currentMonth = currentDate.getMonth();
let currentYear = currentDate.getFullYear();

let months = ["January", "February", "March", "April", "May", "June", "July","August", "September", "October","November", "December"];
let daysOfTheMonth = ["S","M","T","W","T","F","S"];
var datePickers = document.getElementsByTagName("datePicker");

class Calendar {
    constructor(id,year, month) {
        this.id = id,
        this.year = year,
        this.month = month,
        this.header = months[this.month] + " " + this.year;
    }
    previous(calendarBody,calendarHeader,mainHeader){
        this.year = (this.month === 0) ? this.year - 1 : this.year;
        this.month = (this.month === 0) ? 11 : this.month - 1;
        this.update(calendarBody,calendarHeader,mainHeader)
    }
    next(calendarBody,calendarHeader,mainHeader) {
        this.year = (this.month === 11) ? this.year + 1 : this.year;
        this.month = (this.month + 1) % 12;
        this.update(calendarBody,calendarHeader,mainHeader)
    }
    selectDate(date){
        
        let year = this.year;
        let month = this.month;
        let newDate =  new Date(year, month, date)
        const newMonth = newDate.toLocaleString('default', { month: 'short' });
        var dayName = newDate.toString().split(' ')[0];
        document.getElementById("topHeader_"+ this.id).innerHTML = dayName+", "+ newMonth + " "+ date
        document.getElementById("input_"+ this.id).value = formatDate(newDate)
    }
    update(calendarBody,calendarHeader,mainHeader){
        let month = currentDate.toLocaleString('default', { month: 'short' });
        let dayName = currentDate.toString().split(' ')[0];
        mainHeader.innerHTML = dayName+", "+ month + " " + currentDate.getDate();
        
        calendarHeader.innerHTML = months[this.month] + " " + this.year;
        calendarBody.innerHTML = "";
        let firstDay = (new Date(this.year, this.month)).getDay();
            let daysInMonth = 32 - new Date(this.year, this.month, 32).getDate();
            let date = 1;
            for (let i = 0; i < 6; i++) {
                let row = document.createElement("tr");
                for (let j = 0; j < 7; j++) {
                    if (i === 0 && j < firstDay) {
                        let cell = document.createElement("td");
                        cell.innerHTML = ""
                        row.appendChild(cell);
                    }
                    else if (date > daysInMonth) {
                        break;
                    }
                    else {
                        let vm = this;
                        let cell = document.createElement("td");
                        let button = document.createElement("button");
                        button.innerHTML = date;
                        cell.appendChild(button);
                        button.addEventListener('click',function(e){
                          
                            vm.selectDate(e.target.innerHTML);
                        });
                        if (date === currentDate.getDate() && this.year === currentDate.getFullYear() && this.month === currentDate.getMonth()) {
                            cell.classList.add("today");
                        }
                        row.appendChild(cell);
                        date++;
                    }
                }
                calendarBody.appendChild(row);
            }
    }
    createPicker(datePicker){
        let vm = this;
        let mainHeader = document.createElement("div");
            mainHeader.setAttribute("id", "topHeader_"+ vm.id)
            mainHeader.classList.add("topHeader");
            mainHeader.innerHTML = vm.header;

        let calendar = document.createElement("div");
            calendar.setAttribute("id", "calendar_"+vm.id);
            calendar.setAttribute("class", "calendar");
            calendar.style.display = "none";

        calendar.appendChild(mainHeader);

        let input = document.createElement("input");
            input.setAttribute("type", "text");
            input.setAttribute("id", "input_"+ vm.id);
            input.setAttribute("tagName", "input");
            input.setAttribute("class", "datePickerInput");
            input.setAttribute("calendar", "calendar_"+vm.id);
            input.setAttribute("placeholder", "Select a date");

        datePicker.appendChild(input);

        let calendarHeaderContainer = document.createElement("div");
        let prevButton = document.createElement("button");
            prevButton.setAttribute("id", "prevButton_"+vm.id);
            prevButton.innerHTML = "<";

        let nextButton = document.createElement("button");
            nextButton.setAttribute("id", "nextButton_"+vm.id);
            nextButton.innerHTML = ">";
        
        calendarHeaderContainer.appendChild(prevButton);

        let calendarHeader = document.createElement("div");
            calendarHeader.setAttribute("id", "calendar_header_"+vm.id);
            calendarHeader.setAttribute("class", "calendar_header");
            calendarHeader.innerHTML = vm.header;
          
        calendarHeaderContainer.appendChild(calendarHeader);
        calendarHeaderContainer.appendChild(nextButton);

        let calendarTable = document.createElement("table");
            calendarTable.setAttribute("id", "calendar_body_"+vm.id);
           
        let calendarTableBody = calendarTable.createTBody();
    

        let calendarTableHeader = calendarTable.createTHead();
        calendarTableHeader.classList.add("daysOfTheMonth");
        
        
            let row = document.createElement("tr");
            for (let j = 0; j < daysOfTheMonth.length; j++) {
                let cell = document.createElement("td");
                cell.innerHTML = daysOfTheMonth[j];
                row.appendChild(cell);
            }
            calendarTableHeader.appendChild(row);
            this.update(calendarTableBody,calendarHeader,mainHeader)
            
        
        calendar.appendChild(calendarHeaderContainer);
        calendar.appendChild(calendarTable);
        
        datePicker.appendChild(calendar);


    input.addEventListener('click',function(e){
        let calendar = document.getElementById(this.getAttribute("calendar"));
        if(calendar.style.display === "none"){
            calendar.style.display = "block"
        }else{
            calendar.style.display = "none"
        }
        
        });
        nextButton.addEventListener('click',function(e){
            vm.next(calendarTableBody,calendarHeader,mainHeader);
        });

        prevButton.addEventListener('click',function(e){
            vm.previous(calendarTableBody,calendarHeader,mainHeader);
        });
         
    
    }
}

function formatDate(date) {
    var d = new Date(date),
        month = '' + (d.getMonth() + 1),
        day = '' + d.getDate(),
        year = d.getFullYear();

    if (month.length < 2) month = '0' + month;
    if (day.length < 2) day = '0' + day;

    return [year, month, day].join('-');
}

for ( var x = 0; x < datePickers.length; x++) {
   var calendar = new Calendar(x, currentYear, currentMonth);
  calendar.createPicker(datePickers[x])
}
.calendar {
    width: 250px;
    background: gainsboro;
    border-radius: 5px;
}
.calendarHeaderContainer {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.topHeader {
    font-size: 20px;
    background: #03A9F4;
    padding: 10px;
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
}
/*Button Reset*/
button {
    background: none;
    border: none;
}
<h1>Date Picker</h1>
    <datePicker></datePicker>

One thing I do want to know is: I want to return the selected date for each picker and it also has to be by the id of the picker so you know what date goes where.

Thanks again, everyone.

\$\endgroup\$
2
\$\begingroup\$

You probably are familiar with <input type="date"> but maybe you aren't satisfied by the browser compatibility or wanted to try your hand at .

Instead of creating numerous HTML elements with the Javascript Logic, have you considered using a <template> with <slot> tags and replacing values as needed.

Also, I see many variables declared with let but only one with const (i.e. newMonth). It is wise to use const as a default and then when it is determined that re-assignment is necessary switch to let.

Instead of assigning a reference to this in vm use arrow functions (because there would be no separate this context) or else set the context of those anonymous functions to this using Function.bind().

\$\endgroup\$
  • \$\begingroup\$ Sam Thank you so much for this advice i will def keep that in mind. Great great advice. I will def try to use template tags think that is the best way to go \$\endgroup\$ – PeterJoe Sep 3 at 5:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.