如何用真实数据制作每月日历

Avatar of Mateusz Rybczonek
Mateusz Rybczonek

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 价值 200 美元的免费积分!

您是否曾经在网页上看到过日历,然后想,他们是怎么做到的?对于这样的事情,自然会想到使用插件,甚至嵌入 Google 日历,但实际上制作一个比您想象的要简单得多,只需要 HTML、CSS 和 JavaScript 三要素。让我们一起制作一个!

我在 CodeSandbox 上设置了一个演示,您可以看到我们的目标。

首先,让我们确定日历应该完成的一些要求。它应该

  • 显示给定月份的月网格
  • 显示前一个和下一个月的日期,以便网格始终保持完整
  • 指示当前日期
  • 显示当前选定月份的名称
  • 导航到前一个和下一个月
  • 允许用户单击一次即可导航回当前月份

哦,我们还会将其构建为单页应用程序,从 Day.js(一个超级轻量级的实用程序库)获取日历日期。

我们将避免选择特定的框架以保持事情简单。对于此设置,我使用 Parcel 进行包管理,以便我可以在 Babel 中编写,捆绑东西,并管理项目的唯一依赖项。 查看 CodeSandbox 上的 package.json 文件 以了解详细信息。

步骤 1:从基本标记和样式开始

让我们从创建日历的基本模板开始。这不需要任何花哨的东西。但它也应该在不使用表格的情况下完成。

我们可以将我们的标记概述为三层,其中我们有

  • 一个用于日历标题的部分。 这将显示当前选定的月份以及负责在月份之间分页的元素。
  • 一个用于日历网格标题的部分。 再次强调,我们没有使用表格,但这就像一个包含一周中各天的列表的表格标题,从星期一开始。
  • 日历网格。 您知道,当前月份的每一天,都以网格中的一个正方形表示。

让我们在一个名为 index.js 的文件中写出来。这可以放在项目文件夹中的 src 文件夹中。我们确实会在项目根目录中有一个 index.html 文件来导入我们的工作,但主要标记将位于 JavaScript 文件中。

<!-- index.js -->
document.getElementById("app").innerHTML = `
<!-- Parent container for the calendar month -->
<div class="calendar-month">
  <!-- The calendar header -->
  <section class="calendar-month-header">
    <!-- Month name -->
    <div
      id="selected-month"
      class="calendar-month-header-selected-month"
    >
      July 2020
    </div>


    <!-- Pagination -->
    <div class="calendar-month-header-selectors">
      <span id="previous-month-selector"><</span>
      <span id="present-month-selector">Today</span>
      <span id="next-month-selector">></span>
    </div>
  </section>
  
  <!-- Calendar grid header -->
  <ol
    id="days-of-week"
    class="day-of-week"
  >
    <li>Mon</li>
    ...
    <li>Sun</li>
  </ol>


  <!-- Calendar grid -->
  <ol
    id="calendar-days"
    class="date-grid"
  >
    <li class="calendar-day">
      <span>
        1
      </span>
      ...
      <span>
        29
      </span>
    </li>
  </ol>
</div>
`;

让我们继续将此文件导入位于项目根目录中的 index.html 文件。这里没有特殊的事情发生。它只是 HTML 模板,其中包含一个由我们的应用程序定位的元素,并注册我们的 index.js 文件。

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app"></div>

    <script src="src/index.js"></script>
  </body>
</html>

现在我们有一些标记可以处理,让我们对其进行一些样式设置,以便我们有一个良好的视觉效果来开始。具体来说,我们将

  • 使用 flexbox 对元素进行定位
  • 使用 CSS 网格创建日历框架
  • 定位单元格内的标签

首先,让我们在与 index.js 相同的 src 文件夹中创建一个新的 styles.css 文件,并将以下内容放入其中

body {
  --grey-100: #e4e9f0;
  --grey-200: #cfd7e3;
  --grey-300: #b5c0cd;
  --grey-800: #3e4e63;
  --grid-gap: 1px;
  --day-label-size: 20px;
}

.calendar-month {
  position: relative;
  /* Color of the day cell borders */
  background-color: var(--grey-200);
  border: solid 1px var(--grey-200);
}


/* Month indicator and selectors positioning */
.calendar-month-header {
  display: flex;
  justify-content: space-between;
  background-color: #fff;
  padding: 10px;
}


/* Month indicator */
.calendar-month-header-selected-month {
  font-size: 24px;
  font-weight: 600;
}


/* Month selectors positioning */
.calendar-month-header-selectors {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 80px;
}


.calendar-month-header-selectors > * {
  cursor: pointer;
}


/* | Mon | Tue | Wed | Thu | Fri | Sat | Sun | */
.day-of-week {
  color: var(--grey-800);
  font-size: 18px;
  background-color: #fff;
  padding-bottom: 5px;
  padding-top: 10px;
}


.day-of-week,
.days-grid {
  /* 7 equal columns for weekdays and days cells */
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}


.day-of-week > * {
  /* Position the weekday label within the cell */
  text-align: right;
  padding-right: 5px;
}


.days-grid {
  height: 100%;
  position: relative;
  /* Show border between the days */
  grid-column-gap: var(--grid-gap);
  grid-row-gap: var(--grid-gap);
  border-top: solid 1px var(--grey-200);
}


.calendar-day {
  position: relative;
  min-height: 100px;
  font-size: 16px;
  background-color: #fff;
  color: var(--grey-800);
  padding: 5px;
}


/* Position the day label within the day cell */
.calendar-day > span {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  right: 2px;
  width: var(--day-label-size);
  height: var(--day-label-size);
}

设置我们网格的关键部分是

.day-of-week,
.days-grid {
  /* 7 equal columns for weekdays and days cells */
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

请注意,日历网格标题和日历网格本身都使用 CSS 网格来布置。我们知道一周总是七天,因此这允许我们使用 repeat() 函数创建七个彼此成比例的列。我们还在日历的每个日期上声明了一个 min-height 为 100px,以确保行保持一致。

我们需要将这些样式与标记挂钩,因此让我们将其添加到 index.js 文件的顶部

import "./styles.css";

这是一个很好的时机,可以停下来看看我们目前拥有的内容。

步骤 2:设置当前月份日历

您可能已经注意到,模板目前只包含静态数据。月份被硬编码为七月,日期数字也被硬编码。这就是 Day.js 发挥作用的地方。它提供了所有我们需要使用真实日历数据正确放置给定月份的日期在正确星期几列中的数据。它允许我们获取和设置任何内容,从 一个月的开始日期 到所有我们需要显示数据的 日期格式选项

我们将

  • 获取当前月份
  • 计算日期应该放置的位置(星期几)
  • 计算用于显示前一个和下一个月的日期的天数
  • 将所有这些天放在一个数组中

首先,我们需要导入 Day.js 并删除所有静态 HTML(选定月份、星期几和日期)。我们将在 index.js 文件中,导入样式之前的位置添加以下内容

import dayjs from "dayjs";

我们还将依赖一些 Day.js 插件来帮助我们。 WeekDay 帮助我们设置一周的第一天。有些人喜欢将星期日作为一周的第一天。其他人喜欢星期一。哎呀,在某些情况下,从星期五开始是有意义的。我们将从星期一开始。

weekOfYear 插件返回当年中当前周的数字值。一年有 52 周,所以我们会说从 1 月 1 日开始的一周是这一年的第一周,依此类推。

所以,我们在导入语句之后在 index.js 中添加的内容如下

const weekday = require("dayjs/plugin/weekday");
const weekOfYear = require("dayjs/plugin/weekOfYear");


dayjs.extend(weekday);
dayjs.extend(weekOfYear);

删除硬编码的日历值后,到目前为止,我们在 index.js 中拥有以下内容

import dayjs from "dayjs";
import "./styles.css";
const weekday = require("dayjs/plugin/weekday");
const weekOfYear = require("dayjs/plugin/weekOfYear");


dayjs.extend(weekday);
dayjs.extend(weekOfYear);


document.getElementById("app").innerHTML = `
<div class="calendar-month">
  <section class="calendar-month-header">
    <div
      id="selected-month"
      class="calendar-month-header-selected-month"
    >
    </div>
    <div class="calendar-month-header-selectors">
      <span id="previous-month-selector"><</span>
      <span id="present-month-selector">Today</span>
      <span id="next-month-selector">></span>
    </div>
  </section>
  
  <ul
    id="days-of-week"
    class="day-of-week"
  >
  </ul>
  <ul
    id="calendar-days"
    class="days-grid"
  >
  </ul>
</div>
`;

现在让我们设置一些常量。具体来说,我们想构造一个包含一周中各天的数组(即星期一、星期二、星期三等)。

const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

然后,我们想获取当前年份并以 YYYY 格式设置它

const INITIAL_YEAR = dayjs().format("YYYY");

我们希望将当前月份设置为初始加载日历时的起点,其中 M 将月份格式化为数字值(例如,一月等于 1)

const INITIAL_MONTH = dayjs().format("M");

让我们继续用一周中的各天填充我们的日历网格标题。首先,我们获取正确的元素(#days-of-week),然后我们遍历 WEEKDAYS 数组,为数组中的每个项目创建一个列表项元素,同时为每个项目设置名称

// Select the calendar grid header element
const daysOfWeekElement = document.getElementById("days-of-week");


// Loop through the array of weekdays
WEEKDAYS.forEach(weekday => {
  // For each item in the array, make a list item element
  const weekDayElement = document.createElement("li");
  // Append a child element inside the list item...
  daysOfWeekElement.appendChild(weekDayElement);
  /// ...that contains the value in the array
  weekDayElement.innerText = weekday;
});

步骤 3:创建日历网格

这非常简单,但现在真正的乐趣开始了,因为我们将开始处理日历网格。让我们停下来思考一下,要正确地做到这一点,我们真正需要做什么。

首先,我们希望日期数字落入正确的星期几列中。例如,2020 年 7 月 1 日是星期三。这就是日期编号应该开始的地方。

如果该月的第一天是星期三,那么这意味着在第一周的星期一和星期二,我们将有空网格项。该月的最后一天是 7 月 31 日,是星期五。这意味着在网格的最后一周,星期六和星期日将为空。我们要用前一个和下一个月的尾随日期和前导日期分别填充它们,以便日历网格始终保持完整。

创建当前月份的日期

要将当前月份的日期添加到网格中,我们需要知道当前月份中存在多少天。我们可以使用 Day.js 提供的 daysInMonth 方法来获取它。让我们为其创建一个辅助方法。

function getNumberOfDaysInMonth(year, month) {
  return dayjs(`${year}-${month}-01`).daysInMonth()
}

当我们知道这一点后,我们将创建一个长度等于当前月份中天数的空数组。然后,我们 map() 该数组,并为每个元素创建一个日期对象。我们创建的对象具有任意结构,因此如果您需要,可以添加其他属性。

但是,在这个示例中,我们需要一个 date 属性,它将用于检查特定日期是否为当前日期。我们还将返回一个 dayOfMonth 属性,它用作标签(例如 1、2、3 等)。isCurrentMonth 检查日期是否在当前月份内或之外。如果它在当前月份之外,我们将对它们进行样式设置,以便人们知道它们超出了当前月份的范围。

function createDaysForCurrentMonth(year, month) {
  return [...Array(getNumberOfDaysInMonth(year, month))].map((day, index) => {
    return {
      date: dayjs(`${year}-${month}-${index + 1}`).format("YYYY-MM-DD"),
      dayOfMonth: index + 1,
      isCurrentMonth: true
    };
  });
}

将前一个月的日期添加到日历网格

为了在当前月份中显示上个月的日期,我们需要检查所选月份的第一天是星期几。 这就是我们可以使用 Day.js 的 WeekDay 插件的地方。 让我们为此创建一个辅助方法。

function getWeekday(date) {
  return dayjs(date).weekday()
}

然后,基于此,我们需要检查上个月的最后一个星期一是哪一天。 我们需要这个值来知道上个月的多少天应该在当前月份视图中可见。 我们可以通过从当前月份的第一天减去星期几的值来获取它。 例如,如果月份的第一天是星期三,我们需要减去 3 天才能获得上个月的最后一个星期一。 拥有该值使我们能够创建一个从上个月的最后一个星期一开始到该月结束的日期对象数组。

function createDaysForPreviousMonth(year, month) {
  const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].date);


  const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");
  
  // Account for first day of the month on a Sunday (firstDayOfTheMonthWeekday === 0)
  const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday ? firstDayOfTheMonthWeekday - 1 : 6


  const previousMonthLastMondayDayOfMonth = dayjs(
    currentMonthDays[0].date
  ).subtract(visibleNumberOfDaysFromPreviousMonth, "day").date();

  return [...Array(visibleNumberOfDaysFromPreviousMonth)].map((day, index) => {    
    return {
      date: dayjs(
        `${previousMonth.year()}-${previousMonth.month() + 1}-${previousMonthLastMondayDayOfMonth + index}`
      ).format("YYYY-MM-DD"),
      dayOfMonth: previousMonthLastMondayDayOfMonth + index,
      isCurrentMonth: false
    };
  });
}

将下个月的日期添加到日历网格

现在,让我们做相反的事情,计算我们需要从下个月获取的哪些日期才能填充当前月份的网格。 幸运的是,我们可以使用与我们刚刚为上个月计算创建的相同辅助程序。 区别在于我们将通过从 7 中减去星期几的数字值来计算下个月应显示的天数。

因此,例如,如果该月的最后一天是星期六,我们需要从 7 中减去 1 天来构造一个从下个月(星期日)需要的日期数组。

function createDaysForNextMonth(year, month) {
  const lastDayOfTheMonthWeekday = getWeekday(`${year}-${month}-${currentMonthDays.length}`)


  const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday ? 7 - lastDayOfTheMonthWeekday : lastDayOfTheMonthWeekday


  return [...Array(visibleNumberOfDaysFromNextMonth)].map((day, index) => {
    return {
      date: dayjs(`${year}-${Number(month) + 1}-${index + 1}`).format("YYYY-MM-DD"),
      dayOfMonth: index + 1,
      isCurrentMonth: false
    }
  })
}

好的,我们知道如何创建我们需要的全部日期,让我们使用我们刚刚创建的方法,然后将所有日期合并成一个包含我们想要在当前月份显示的所有日期的单个数组,包括来自上个月和下个月的填充日期。

let currentMonthDays = createDaysForCurrentMonth(INITIAL_YEAR, INITIAL_MONTH)
let previousMonthDays = createDaysForPreviousMonth(INITIAL_YEAR, INITIAL_MONTH, currentMonthDays[0])
let nextMonthDays = createDaysForNextMonth(INITIAL_YEAR, INITIAL_MONTH)


let days = [...this.previousMonthDays, ...this.currentMonthDays, ...this.nextMonthDays]

以下是我们在 index.js 中整理的所有内容

// Same as before ...


const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const INITIAL_YEAR = dayjs().format("YYYY");
const INITIAL_MONTH = dayjs().format("M");
const daysOfWeekElement = document.getElementById("days-of-week");


// Add weekdays to calendar header
WEEKDAYS.forEach(weekday => {
  const weekDayElement = document.createElement("li");
  daysOfWeekElement.appendChild(weekDayElement);
  weekDayElement.innerText = weekday;
});


let currentMonthDays = createDaysForCurrentMonth(INITIAL_YEAR, INITIAL_MONTH);
let previousMonthDays = createDaysForPreviousMonth(INITIAL_YEAR, INITIAL_MONTH);
let nextMonthDays = createDaysForNextMonth(INITIAL_YEAR, INITIAL_MONTH);
let days = [...previousMonthDays, ...currentMonthDays, ...nextMonthDays];


console.log(days);


function getNumberOfDaysInMonth(year, month) {
  return dayjs(`${year}-${month}-01`).daysInMonth();
}


function createDaysForCurrentMonth(year, month) {
  return [...Array(getNumberOfDaysInMonth(year, month))].map((day, index) => {
    return {
      date: dayjs(`${year}-${month}-${index + 1}`).format("YYYY-MM-DD"),
      dayOfMonth: index + 1,
      isCurrentMonth: true
    };
  });
}


function createDaysForPreviousMonth(year, month) {
  const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].date);
  const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");

  // Cover first day of the month being sunday (firstDayOfTheMonthWeekday === 0)
  const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday
    ? firstDayOfTheMonthWeekday - 1
    : 6;

  const previousMonthLastMondayDayOfMonth = dayjs(currentMonthDays[0].date)
    .subtract(visibleNumberOfDaysFromPreviousMonth, "day")
    .date();


  return [...Array(visibleNumberOfDaysFromPreviousMonth)].map((day, index) => {
    return {
      date: dayjs(
        `${previousMonth.year()}-${previousMonth.month() +
          1}-${previousMonthLastMondayDayOfMonth + index}`
      ).format("YYYY-MM-DD"),
      dayOfMonth: previousMonthLastMondayDayOfMonth + index,
      isCurrentMonth: false
    };
  });
}

function createDaysForNextMonth(year, month) {
  const lastDayOfTheMonthWeekday = getWeekday(
    `${year}-${month}-${currentMonthDays.length}`
  );
  const nextMonth = dayjs(`${year}-${month}-01`).add(1, "month");
  const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday
    ? 7 - lastDayOfTheMonthWeekday
    : lastDayOfTheMonthWeekday;
  return [...Array(visibleNumberOfDaysFromNextMonth)].map((day, index) => {
    return {
      date: dayjs(
        `${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`
      ).format("YYYY-MM-DD"),
      dayOfMonth: index + 1,
      isCurrentMonth: false
    };
  });
}

function getWeekday(date) {
  return dayjs(date).weekday();
}

步骤 4:显示日历日期

好的,所以我们有了日历的基本标记,我们需要显示当前月份的日期的数据,以及来自上个月和下个月的日期来填充空的网格项。 现在我们需要将日期附加到日历!

我们已经有了日历网格的容器 #calendar-days。 让我们获取该元素。

const calendarDaysElement = document.getElementById("calendar-days");

现在,让我们创建一个将一天附加到日历视图的函数。

function appendDay(day, calendarDaysElement) {
  const dayElement = document.createElement("li");
  const dayElementClassList = dayElement.classList;


  // Generic calendar day class
  dayElementClassList.add("calendar-day");


  // Container for day of month number
  const dayOfMonthElement = document.createElement("span");


  // Content
  dayOfMonthElement.innerText = day.dayOfMonth;


  // Add an extra class to differentiate current month days from prev/next month days
  if (!day.isCurrentMonth) {
    dayElementClassList.add("calendar-day--not-current");
  }


  // Append the element to the container element
  dayElement.appendChild(dayOfMonthElement);
  calendarDaysElement.appendChild(dayElement);
}

请注意,我们正在添加对来自上个月和下个月的日期的检查,以便我们可以添加一个类以将其与当前月份的日期区分开来

.calendar-day--not-current {
  background-color: var(--grey-100);
  color: var(--grey-300);
}

就是这样! 我们的日历现在应该按我们想要的方式显示。

步骤 5:选择当前月份

到目前为止,我们所拥有的已经非常不错了,但是我们希望用户能够在时间上向前和向后翻页,从当前月份开始。 我们已经拥有大部分逻辑,所以我们真正需要做的就是添加一个 click 监听器到分页按钮,该监听器重新运行日期计算并使用更新的数据重新绘制日历。

在我们开始之前,让我们定义变量来表示当前月份、上个月和下个月的日期,以便我们可以在整个代码中引用它们。

let currentMonthDays;
let previousMonthDays;
let nextMonthDays;

现在,让我们创建一个负责重新计算日历日期并在分页到另一个月份时重新渲染日历的方法。 我们将调用该函数 createCalendar。 此方法将接受两个属性——年份和月份——并基于此,日历将使用新数据重新渲染,而无需重新加载页面。

该方法将替换标题内容,以始终显示选定月份的标签。

function createCalendar(year = INITIAL_YEAR, month = INITIAL_MONTH) {
  document.getElementById("selected-month").innerText = dayjs(
    new Date(year, month - 1)
  ).format("MMMM YYYY");


  // ...

然后它将获取日历日期容器并删除所有现有的日期。

// ...


  const calendarDaysElement = document.getElementById("calendar-days");
  removeAllDayElements(calendarDaysElement);


  // ...

清除日历后,它将使用我们之前创建的方法计算应该显示的新日期。

//...


currentMonthDays = createDaysForCurrentMonth(
  year,
  month,
  dayjs(`${year}-${month}-01`).daysInMonth()
);


previousMonthDays = createDaysForPreviousMonth(year, month);


nextMonthDays = createDaysForNextMonth(year, month);


const days = [...previousMonthDays, ...currentMonthDays, ...nextMonthDays];


// ...

最后,它将为每个日期附加一个日期元素。

// ...
days.forEach(day => {
  appendDay(day, calendarDaysElement);
});

还缺少一个逻辑部分:一个 removeAllDayElements 方法,它清除现有的日历。 此方法获取第一个日历日期元素,将其删除,并用另一个替换它。 从那里,它将在循环中运行逻辑,直到所有元素都被删除。

function removeAllDayElements(calendarDaysElement) {
  let first = calendarDaysElement.firstElementChild;


  while (first) {
    first.remove();
    first = calendarDaysElement.firstElementChild;
  }
}

现在,当我们想要更改月份时,我们可以重复使用该逻辑。 回想一下我们创建组件的静态模板的第一个步骤。 我们添加了这些元素

<div class="calendar-month-header-selectors">
  <span id="previous-month-selector"><</span>
  <span id="present-month-selector">Today</span>
  <span id="next-month-selector">></span>
</div>

这些是月份之间分页的控件。 要更改它,我们需要存储当前选定的月份。 让我们创建一个变量来跟踪它是什么,并将它的初始值设置为当前月份。

let selectedMonth = dayjs(new Date(INITIAL_YEAR, INITIAL_MONTH - 1, 1));

现在,为了使选择器正常工作,我们需要一些 JavaScript 代码。 为了使其更易读,我们将创建另一个名为 initMonthSelectors 的方法,我们将把逻辑保留在那里。 此方法将向选择器元素添加事件监听器。 它将监听点击事件,并在运行具有正确年份和月份值的 createCalendar 方法之前,将 selectedMonth 的值更新为新选定月份的名称。

function initMonthSelectors() {
  document
  .getElementById("previous-month-selector")
  .addEventListener("click", function() {
    selectedMonth = dayjs(selectedMonth).subtract(1, "month");
    createCalendar(selectedMonth.format("YYYY"), selectedMonth.format("M"));
  });


  document
  .getElementById("present-month-selector")
  .addEventListener("click", function() {
    selectedMonth = dayjs(new Date(INITIAL_YEAR, INITIAL_MONTH - 1, 1));
    createCalendar(selectedMonth.format("YYYY"), selectedMonth.format("M"));
  });


  document
  .getElementById("next-month-selector")
  .addEventListener("click", function() {
    selectedMonth = dayjs(selectedMonth).add(1, "month");
    createCalendar(selectedMonth.format("YYYY"), selectedMonth.format("M"));
  });
}

就是这样! 我们的日历准备好了。 虽然这很好,但是如果我们可以标记当前日期使其从其他日期中脱颖而出,那就更好了。 这应该不太难。 我们已经在对当前月份以外的日期进行样式设置,所以让我们做类似的事情。

我们将创建一个设置为今天的变量

const TODAY = dayjs().format("YYYY-MM-DD");

然后,在我们在 appendDay 方法中为当前月份之外的日期应用类的地方,我们必须添加另一个检查以查看该元素是否为今天的日期。 如果是,我们将向该元素添加一个类

function appendDay(day, calendarDaysElement) {
  // ...
  if (day.date === TODAY) {
    dayElementClassList.add("calendar-day--today");
  }
}

现在我们可以为它设置样式了!

.calendar-day--today {
  padding-top: 4px;
}


.calendar-day--today > div {
  color: #fff;
  border-radius: 9999px;
  background-color: var(--grey-800);
}

,就是这样! 查看最终演示以查看所有内容的组合。