Native JS implements a calendar class

Recently, I have a need to make a calendar to display, although there is a ready-made online, but I still want to implement one by myself. For this small plug-in, my idea is to give the operation permission to the user, the plug-in only provides the business logic, to achieve the separation of business logic from the UI layer. It is also compatible with all frameworks.
Before writing, consider which operation functions to provide to the user. I have listed the following arbitrarily:
  • PrevMonth on your calendar.
  • NextMonth on your calendar
  • PrevYear on your Calendar
  • NextYear in the calendar
  • Calendar render
  • Other (free play…)
That’s it. Let’s start by defining a class:
/ / the calendar class
class Calendar {
  constructor(option) {
		// Some attributes
  }
  
  // Operate the function
  prevMonth(){}nextMonth(){}prevYear(){}nextYear(){}render(){}}Copy the code
Next, define some attributes:
constructor(option) {
  // Some attributes
  this.currentMonth = 0; / / that month
  this.currentYear = 1996; / / the
  this.currentFirstWeekDay = 1; // What day is the first day of the month
  this.renderCallback = null; // The render callback passed in by the user
  this.allDay = 31; // How many days this month
}
Copy the code
Put the operations that change the values of these attributes in an init function, and define an init function:
init(date) {
  // date, a date object. Calendar changes are the date of the operation, which will be called by subsequent operation functions
  this.currentYear = date.getFullYear();
  this.currentMonth = date.getMonth();
  this.currentFirstWeekDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay();
  this.allDay = new Date( date.getFullYear(), date.getMonth() + 1.0).getDate();
}
Copy the code
Class is called first when executed:
constructor(option) {
  // Some attributes
  this.currentMonth = 0; / / that month
  this.currentYear = 1996; / / the
  this.currentFirstWeekDay = 1; // What day is the first day of the month
  this.allDay = 31; // How many days this month
  this.renderCallback = null; // The render callback passed in by the user
  this.init(new Date()); // The first time it is executed, the current date is passed
}
Copy the code
The focus of the calendar is data. I use the common 6 * 7 two-dimensional array to display, that is to say, 42 pieces of information are displayed every month, including the following:
  • Number of days to show this month
  • Number of days to show last month
  • Number of days to show next month
So create three more utility functions that return the number of days each needs to display:
/* Sunday = 0; /* Sunday = 0; And our calendar is also days, one, two, three, four, five, six, so calculate the number of days to display in the last month is directly the first day of the month on the day of the week. For example, if the first day is on Saturday, the last month will show 6 days, and on Sunday, it will show 0 days. The number of days in the next month is simple, that is, the total number of days displayed (42) - the number of days displayed in the last month - the number of days displayed in the current month */

// Determine if a date in the current calendar is the actual date
checknowDay(year, month, day) {
  let date = new Date(a);let nowYear = date.getFullYear();
  let nowMonth = date.getMonth();
  let nowDay = date.getDate();
  if (nowYear === year && nowMonth === month && nowDay === day) {
    return true;
  }else {
    return false; }}getPrevMonthDay() {
  // This function is used to get the number of days to display in the last month in the calendar
  let date = new Date(this.currentYear, this.currentMonth, 0); // Get last month;
  let day = date.getDate(); // Because the day of the Date function is set to 0, this is the value of the last day of the last month
  let days = this.currentFirstWeekDay;// How many days is displayed last month
  let weeks = [];
  while (days > 0) {
    weeks.push({
      year: date.getFullYear(),
      month: date.getMonth() + 1.day: day --,
      type: 'prev'.// It is used to mark this as last month, and can be used with rendering processing, such as graying
    });
    days --;
  }
  return weeks.sort((a, b) = > a.day - b.day); // The order is from the largest to the smallest
}

getNowMonthDay() {
  // This function is used to get the number of days to display in a calendar month
  let days = this.allDay;
  let weeks = [];
  let day = 1;
  while (days > 0) {
    let check = this.checknowDay(this.currentYear, this.currentMonth, day);
    weeks.push({
      year: this.currentYear,
      month: this.currentMonth + 1.day: day ++,
      type: check ? 'current' : ' '.// Determine whether a certain day of the month corresponds to the actual day
    });
    days --;
  }
  return weeks;
}

getNextMonthDay() {
	// This function is used to get the number of days to display in the next month in the calendar
  let days = 42 - this.currentFirstWeekDay - this.allDay;
  let date = new Date(this.currentYear, this.currentMonth + 1.1); // Get next month
  let weeks = [];
  let day = 1;
  while (days > 0) {
    weeks.push({
      year: date.getFullYear(),
      month: date.getMonth() + 1.day: day ++,
      type: 'next'}); days --; }return weeks;
}
Copy the code
After writing these methods, we will expose the operation function:
The /* operator calls two methods each time it executes, the init function to update the date and the render function */

// Operate the function
prevMonth() {
	if (this.currentMonth === 0) {
    this.currentMonth = 11;
    this.currentYear --;
  }else {
    this.currentMonth --;
  }
  this.init(new Date(this.currentYear, this.currentMonth));
  this.render(this.renderCallback);
}

nextMonth() {
	if (this.currentMonth === 11) {
    this.currentMonth = 0;
    this.currentYear ++;
  }else {
    this.currentMonth ++;
  }
  this.init(new Date(this.currentYear, this.currentMonth));
  this.render(this.renderCallback);
}

prevYear() {
	this.currentYear --;
  this.init(new Date(this.currentYear, this.currentMonth));
  this.render(this.renderCallback);
}

nextYear() {
	this.currentYear ++;
  this.init(new Date(this.currentYear, this.currentMonth));
  this.render(this.renderCallback);
}

// Render function, which the user must use to get the latest date data after creating a new instance, receives a callback function
render(fn) {
	if (typeoffn ! = ='function') {
    throw new Error('Parameter must be a function');
  }
  let weeks = [...this.getPrevMonthDay(), ...this.getNowMonthDay(), ...this.getNextMonthDay()];
  let data = [];
  let count = [];
  // Convert a 1-d array into a 2-D array
  for (let i = 0; i < weeks.length; i++) {
    if (count.length === 6) {
      count.push(weeks[i]);
      data.push(count);
      count = [];
    }
    else {
      count.push(weeks[i]);
    }
  }
  fn(data);
  // Be sure to save the function passed in by the user, so that it can be used when operating on function calls
  if (!this.renderCallback) {
    this.renderCallback = fn; }}Copy the code
Now that you’re done, create an HTML to run:
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>The calendar</title>
    <link rel="stylesheet" href="./Calendar.css">
</head>
<body>
        <div class="calendar-wrap">
          <div class="show-text">
            <h1>Current calendar:</h1>
            <h2 id="date" style="text-align: center;"></h2>
          </div>
            <table id="calendar1">
                <thead>
                    <tr>
                        <th><div class="calendar-wrap-header-item">Sunday</div></th>
                        <th><div class="calendar-wrap-header-item">Monday</div></th>
                        <th><div class="calendar-wrap-header-item">Tuesday</div></th>
                        <th><div class="calendar-wrap-header-item">Wednesday</div></th>
                        <th><div class="calendar-wrap-header-item">Thursday</div></th>
                        <th><div class="calendar-wrap-header-item">Friday</div></th>
                        <th><div class="calendar-wrap-header-item">Saturday</div></th>
                    </tr>
                </thead>
            </table>
          
            <div class="calendar-wrap-tools">
                <div id="prevYear" class="prev-year">Over a year</div>
                <div id="prevMonth" class="prev-month">Turn on January</div>
                <div id="nextMonth" class="next-month">Under the double January</div>
                <div id="nextYear" class="next-year">Over a year</div>
            </div>
        </div>


    <script src="./Calendar.js"></script>

    <script>

        let table = document.getElementById('calendar1');
        let date = document.getElementById('date');
        let prevMonth = document.getElementById('prevMonth');
        let nextMonth = document.getElementById('nextMonth');
        let prevYear = document.getElementById('prevYear');
        let nextYear = document.getElementById('nextYear');


        let calendar1 = new Calendar();
        calendar1.render((data) = > {
            let tbody = document.createElement('tbody');
            for (let i = 0; i < data.length; i++) {
                let tr = document.createElement('tr');
                for (let j = 0; j < data[i].length; j++) {
                    let td = document.createElement('td');
                    let inner = `<div class="calendar-wrap-body-item ${data[i][j].type}"><span>${data[i][j].day}</span></div>`;
                    td.innerHTML = inner;
                    tr.appendChild(td);
                }
                tbody.appendChild(tr);
            }
            if (table.getElementsByTagName('tbody') [0]) {
                table.removeChild(table.getElementsByTagName('tbody') [0]);
            }
            table.appendChild(tbody);
            date.innerHTML = `${calendar1.currentYear}${calendar1.currentMonth + 1}Month `; // The page displays the current date
        })

        prevMonth.addEventListener('click'.(e) = > {
            calendar1.prevMonth();
        })

        nextMonth.addEventListener('click'.(e) = > {
            calendar1.nextMonth();
        })

        prevYear.addEventListener('click'.(e) = > {
            calendar1.prevYear();
        })

        nextYear.addEventListener('click'.(e) = > {
            calendar1.nextYear();
        })
        

    </script>
</body>
</html>
Copy the code
My CSS, written blind:
* {padding: 0;
    margin: 0;
}


.calendar-wrap{}.calendar-wrap table{
    table-layout: fixed;
    width: 100%;
    border-collapse: collapse;
}

.calendar-wrap table thead th{
    border: 1px solid rgb(202.202.202);
}
.calendar-wrap table tbody td{
    border: 1px solid rgb(202.202.202);
}

.calendar-wrap .calendar-wrap-header-item{
    background-color: bisque;
    padding: 15px;
}

.calendar-wrap .calendar-wrap-body-item{
    padding: 15px;
    text-align: center;
    background-color: rgb(255.255.255);
}
.calendar-wrap .calendar-wrap-body-item span{
    border-radius: 50%;
    display: inline-block;
    width: 40px;
    height: 40px;
    text-align: center;
    line-height: 40px;
    color: # 888;
    /* background: rgb(11, 102, 206); * /
}

.calendar-wrap .calendar-wrap-body-item.prev span{
    background: rgb(216.216.216);
    color: #fff;
}

.calendar-wrap .calendar-wrap-body-item.current span{
    background: rgb(255.3.3);
    color: #fff;
}

.calendar-wrap .calendar-wrap-body-item.next span{
    background: rgb(216.216.216);
    color: #fff;
}


.calendar-wrap .calendar-wrap-tools{
    display: flex;
    align-items: center;
    height: 50px;
    background: cornsilk;
}


.prev-year..prev-month..next-month..next-year{
    width: 100px;
    height: 30px;
    margin: 0 5px;
    line-height: 30px;
    border: 1px solid rgb(17.108.212);
    background-color: rgb(17.108.212);
    color: #fff;
    font-family:'Franklin Gothic Medium'.'Arial Narrow', Arial, sans-serif;
    text-align: center;
}
Copy the code
Plug-in class:
/ / the calendar class
class Calendar {
    constructor(option) {
        // Some attributes
        this.currentMonth = 0; / / that month
        this.currentYear = 1996; / / the
        this.currentFirstWeekDay = 1; // What day is the first day of the month
        this.renderCallback = null; // The render callback passed in by the user
        this.allDay = 31; // How many days this month
        this.init(new Date()); // The first time it is executed, the current date is passed
    }
    init(date) {
        // date, a date object. Calendar changes are the date of the operation, which will be called by subsequent operation functions
        this.currentYear = date.getFullYear();
        this.currentMonth = date.getMonth();
        this.currentFirstWeekDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay();
        this.allDay = new Date( date.getFullYear(), date.getMonth() + 1.0).getDate();
    }

    checknowDay(year, month, day) {
        let date = new Date(a);let nowYear = date.getFullYear();
        let nowMonth = date.getMonth();
        let nowDay = date.getDate();
        if (nowYear === year && nowMonth === month && nowDay === day) {
          return true;
        }else {
          return false; }}getPrevMonthDay() {
        // This function is used to get the number of days to display in the last month in the calendar
        let date = new Date(this.currentYear, this.currentMonth, 0); // Get last month;
        let day = date.getDate(); // Because the day of the Date function is set to 0, this is the value of the last day of the last month
        let days = this.currentFirstWeekDay;// How many days is displayed last month
        let weeks = [];
        while (days > 0) {
          weeks.push({
            year: date.getFullYear(),
            month: date.getMonth() + 1.day: day --,
            type: 'prev'.// It is used to mark this as last month, and can be used with rendering processing, such as graying
          });
          days --;
        }
        return weeks.sort((a, b) = > a.day - b.day); // The order is from the largest to the smallest
      }
      
      getNowMonthDay() {
        // This function is used to get the number of days to display in a calendar month
        let days = this.allDay;
        let weeks = [];
        let day = 1;
        while (days > 0) {
          let check = this.checknowDay(this.currentYear, this.currentMonth, day);
          weeks.push({
            year: this.currentYear,
            month: this.currentMonth + 1.day: day ++,
            type: check ? 'current' : ' '.// Determine whether a certain day of the month corresponds to the actual day
          });
          days --;
        }
        return weeks;
      }
      
      getNextMonthDay() {
          // This function is used to get the number of days to display in the next month in the calendar
        let days = 42 - this.currentFirstWeekDay - this.allDay;
        let date = new Date(this.currentYear, this.currentMonth + 1.1); // Get next month
        let weeks = [];
        let day = 1;
        while (days > 0) {
          weeks.push({
            year: date.getFullYear(),
            month: date.getMonth() + 1.day: day ++,
            type: 'next'}); days --; }return weeks;
      }
    
    // Operate the function
    prevMonth() {
        if (this.currentMonth === 0) {
        this.currentMonth = 11;
        this.currentYear --;
      }else {
        this.currentMonth --;
      }
      this.init(new Date(this.currentYear, this.currentMonth));
      this.render(this.renderCallback);
    }
    
    nextMonth() {
        if (this.currentMonth === 11) {
        this.currentMonth = 0;
        this.currentYear ++;
      }else {
        this.currentMonth ++;
      }
      this.init(new Date(this.currentYear, this.currentMonth));
      this.render(this.renderCallback);
    }
    
    prevYear() {
        this.currentYear --;
      this.init(new Date(this.currentYear, this.currentMonth));
      this.render(this.renderCallback);
    }
    
    nextYear() {
        this.currentYear ++;
      this.init(new Date(this.currentYear, this.currentMonth));
      this.render(this.renderCallback);
    }
    
    // Render function, which the user must use to get the latest date data after creating a new instance, receives a callback function
    render(fn) {
        if (typeoffn ! = ='function') {
        throw new Error('Parameter must be a function');
      }
      let weeks = [...this.getPrevMonthDay(), ...this.getNowMonthDay(), ...this.getNextMonthDay()];
      let data = [];
      let count = [];
      // Convert a 1-d array into a 2-D array
      for (let i = 0; i < weeks.length; i++) {
        if (count.length === 6) {
          count.push(weeks[i]);
          data.push(count);
          count = [];
        }
        else {
          count.push(weeks[i]);
        }
      }
      fn(data);
      // Be sure to save the function passed in by the user, so that it can be used when operating on function calls
      if (!this.renderCallback) {
        this.renderCallback = fn; }}}Copy the code
It looks like this:

This is the end of a simple calendar. The advantage of this is that the logic is encapsulated, the plugin does not render, and it can be adapted to a variety of application scenarios (for example, how good the calendar looks depends on the individual level, not the plugin). Other customizations required can also be extended on this basis. Please forgive me if I don’t write well. It’s not easy to create something original.