前端练手案例之日历(原生)一

介绍

用原生三件套从零到一实现以下日历模块,样式如图
在这里插入图片描述
目前具备以下功能

  1. 展示当前年月日,并对当前日其展示激活状态
  2. 点击上一月 下一月进行跳转
完成骨架搭建

首先先对,整体样式进行构思(对于毫无艺术细胞的我来说,差点要了半条命),最后决定先按照上图先做着,后边要改再说,日期内容采用的是表格进行渲染

    <div id="calendar">
      <div class="head">
        <div class="pre-month">上一月</div>
        <div class="date">
    	  <!-- 这里先用的是死数据 -->
          <div class="year">2022</div>
          <div class="month">10月</div>
        </div>
        <div class="next-month">下一月</div>
      </div>
      <div class="main">
        <table>
          <thead>
            <tr>
              <!-- 表格关于周几的就直接写上不生成了 -->
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
              <td></td>
            </tr>
          </thead>
          <tbody></tbody>
        </table>
      </div>
    </div>
添加样式

样式就按照上面那个不太好看的样式来就行,因为是表格,所以布局的内容比较少

      #calendar {
        display: flex;
        flex-direction: column;
        width: 100vw;
        box-sizing: border-box;
        padding: 10px;
        margin: 26px auto;
        background-color: #f49bc4;
      }
      /* 标题部分 */
      #calendar .head {
        display: flex;
        width: 100%;
        margin-top: 10px;
        justify-content: space-evenly;
        font-size: 22px;
        font-weight: 700;
        color: #50508a90;
      }
      #calendar .head .pre-month,
      #calendar .head .next-month,
      #calendar .head .pre-month .year,
      #calendar .head .month {
        cursor: pointer;
      }
      /* 日期部分 */
      #calendar .main {
        width: 100%;
        color: aliceblue;
        font-size: 20px;
        font-weight: 900;
        margin-top: 20px;
        text-align: center;
      }
      #calendar .main table {
        width: 100%;
        color: #50508a90;
      }
      #calendar .main table tbody tr {
        width: 100%;
      }
      #calendar .main table tbody tr td {
        height: 36px;
        line-height: 36px;
        color: #fff;
      }
      .active {
        background-color: #50508a90;
      }
日期部分

这个练习案例,最主要的就是对于js内建对象Date的使用了

当前日期

首先我们需要拿到当前日期,然后一层一层的拿到以下内容:

  1. 今天星期几
  2. 当前月份有多少天
  3. 这个月的第一天是星期几
    获取当前日期是比较简单的
const date = new Date()
const year = date.getFullYear()  // 年份
const month = date.getMonth() + 1 // 月份
const weekDay = date.getDay() // 周几
const day = date.getDate() // 日期

后面在多个位置都需要根据日期获取这些数据,所以我把他抽成了一个函数,通过外界传入的日期对象获取对应的相关数据

function getDate(date) {
  const year = date.getFullYear() // 年份
  const month = date.getMonth() + 1 // 月份
  const weekDay = date.getDay() // 周几
  const day = date.getDate() // 日期
  return {
    year,
    month,
    weekDay,
    day,
  }
}

注意:Date对象的 getMonth()获取到的月数是从零开始的

获取本月的天数

获取当前月数的方法,我想半天没想到,后来查MDN(还是对原生Date对象的用法不太熟悉),找到了setDate方法,该方法根据本地时间来指定一个日期对象的天数,如果当我们传入的天数超出了该月的合理范围,它会根据一定规则进行更新对象

原时间对象会被改变

  1. 传入 0 :设置为上一个月的最后一天
  2. -1 :设置为上一个月的倒数第二天
  3. 32 :超出了当月的范围,往下一个月类推

根据上面的特点,所以我们可有下面的方法来获取某个月的天数

function getMonthDays(date) {
  // 将日期设置为32,表示自动计算为下个月的第几天(这取决于当前月份有多少天)
  let oldDate = new Date()
  date.setDate(32)
  let days = 32 - date.getDate()
  let dayArrs = []
  let i = 1
  while (i <= days) {
    dayArrs.push(oldDate.setDate(i))
    i++
  }
  // 返回当前月份的天数
  return {
    days,
    dayArrs,
  }
}

在这个方法中,不仅获取到了天数,也将每个日期对应的时间戳返回了

获取第一天的星期数

对于这个功能没有单独抽取方法,大概思路就是,使用setDate方法,将日期设置为1,然后getDay

// 这里使用 copyDate 是因为 setDate会改变源对象,影响了后面的操作,开始找半天没找到哪里不对
let firstDay = new Date(copyDate.setDate(1)).getDay()
收尾工作

编写一个renderDate函数用于渲染操作,同时也方便在其他方法中调用(如事件,初始化操作),这里用了表格,所以就根据日期的特点,采用的是6 x 7的表格,所以里边套了两层循环,在循环中获取了哪一个日期是当前活跃的

 function renderDate(date) {
        const copyDate = new Date(date) // 拷贝一份,防止后面一不小心改了
        tbody.innerHTML = "" // 清空之前的内容 T
        const { year, month, day } = getDate(date)
        // 获取当前的年月日
        const { dayArrs, days } = getCountDays(date)
        // 获取本月所有的时间戳, 以及本月的天数

        //#region
        currentMonth = month // 记录状态
        currentYear = year // 记录状态
        //#endregion

        //#region  头部展示部分
        yearEl.innerHTML = year
        monthEl.innerHTML = month + "月"
        //#endregion

        // 用于统计当前已经渲染上的格子数
        let count = 0
        // 本月的第一天的星期数
        let firstDay = new Date(copyDate.setDate(1)).getDay()
        // 用于暂存创建的 tr 元素
        let trs = document.createDocumentFragment()
        for (let i = 0; i < 6; i++) {
          // 第一层循环代表行数
          let tr = document.createElement("tr")
          // 用于暂存创建的 td 元素
          let tds = document.createDocumentFragment()
          for (let j = 0; j < 7; j++) {
            // 这一层表示列数
            let td = document.createElement("td")
            // 当前日期的星期数
            let day = new Date(dayArrs[count]).getDay()
            if (i === 0 && j < firstDay) {
              // 这里的判断用于跳过 1 号之前的日期格子
              tds.append(td)
              continue
            }
            if (count < days) {
              // 如果以渲染的格子数小于天数就继续添加
              if (new Date(dayArrs[count]).getDate() === new Date().getDate()) {
                // 将当前日期设置为激活状态
                td.classList.add("active")
              }
              td.innerHTML = new Date(dayArrs[count]).getDate()
            }
            tds.append(td)
            count++
          }
          tr.append(tds)
          trs.append(tr)
        }
        tbody.append(trs)
      }

完成该函数之后,我们通过一个IIFE进行初始化操作

(function () {
        let date = new Date() // 获取当前日期
        renderDate(date)
 })()

结束语

到这里,具有基本功能的日历差不多就实现了,虽然功能比较简单,但是我还是花了好长时间完成,不过也算是对Date熟悉了些了,这波不亏!后续完成更加复杂的功能
在这里插入图片描述

简单的日历组件

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的前端小程序平铺日历代码: HTML: ``` <div class="calendar"> <div class="calendar-header"> <div class="prev-month"><</div> <div class="current-month">July 2021</div> <div class="next-month">></div> </div> <div class="calendar-body"> <div class="calendar-weekdays"> <div class="weekday">Sun</div> <div class="weekday">Mon</div> <div class="weekday">Tue</div> <div class="weekday">Wed</div> <div class="weekday">Thu</div> <div class="weekday">Fri</div> <div class="weekday">Sat</div> </div> <div class="calendar-days"></div> </div> </div> ``` CSS: ``` .calendar { border: 1px solid #ccc; width: 300px; font-family: Arial, sans-serif; } .calendar-header { display: flex; justify-content: space-between; align-items: center; background-color: #eee; padding: 10px; } .calendar-header div { cursor: pointer; } .current-month { font-weight: bold; } .calendar-weekdays { display: flex; justify-content: space-between; background-color: #eee; padding: 10px; } .weekday { font-weight: bold; } .calendar-days { display: flex; flex-wrap: wrap; padding: 10px; } .day { width: 14.28%; text-align: center; padding: 5px; } .current-day { background-color: #ccc; } ``` JavaScript: ``` const calendarBody = document.querySelector('.calendar-days'); const prevMonth = document.querySelector('.prev-month'); const nextMonth = document.querySelector('.next-month'); const currentMonth = document.querySelector('.current-month'); let date = new Date(); let year = date.getFullYear(); let month = date.getMonth(); renderCalendar(year, month); prevMonth.addEventListener('click', () => { month--; if (month < 0) { year--; month = 11; } renderCalendar(year, month); }); nextMonth.addEventListener('click', () => { month++; if (month > 11) { year++; month = 0; } renderCalendar(year, month); }); function renderCalendar(year, month) { const daysInMonth = new Date(year, month + 1, 0).getDate(); const firstDayOfMonth = new Date(year, month, 1).getDay(); const lastDayOfMonth = new Date(year, month, daysInMonth).getDay(); currentMonth.textContent = `${months[month]} ${year}`; let days = ''; for (let i = 1; i <= daysInMonth + firstDayOfMonth; i++) { const day = i - firstDayOfMonth; if (i > firstDayOfMonth) { if (i <= daysInMonth + firstDayOfMonth) { days += `<div class="day">${day}</div>`; } } else { days += `<div class="day"></div>`; } } if (lastDayOfMonth !== 6) { const daysToAdd = 6 - lastDayOfMonth; for (let i = 0; i < daysToAdd; i++) { days += `<div class="day"></div>`; } } calendarBody.innerHTML = days; } ``` 这段代码实现了一个简单的平铺式日历,可以通过左右箭头按钮来切换月份。每个月份的日期都按照平铺式排列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值