使用 Vanilla Javascript 制作日历
可以说,时间函数是 JavaScript 中最好也是最差的内置函数。在我最近的项目中,我萌生了一个想法,想做一个功能更强大的 Google 日历。我天真地以为,做一个通用的、功能齐全的日历会很简单,而且能让我有足够的时间来构建我的网站。结果我错了。
从哪里开始?
很早以前,在导师的指导下,我决定将月份信息硬编码到 HTML 中。这样我就能构建出我想要的日历的总体结构和样式。关于使用<div>
或<table>
是否更好的做法,文献中存在争议。最终我决定使用表格。
如果你曾经花时间认真研究过月历,就会发现它的结构其实很简单。通常顶部有一个标题,用于指示月份和年份。标题下方是 7 列,分别表示一周中的每一天。最后,最多有 6 行用于填充给定月份的天数。
下面是我的硬编码 HTML 的快照。
再次,这允许我弄乱 CSS 样式表来呈现我想要的日历的整体外观和感觉。
利用时间的力量
现在到了最难的部分。我需要使用内置的时间函数来填充给定月份和年份的准确天数。经过一番研究,我发现 JavaScript 会返回一个对应于月份在一年中的相对索引的数字:例如,一月是 0,二月是 1,等等。这可以通过getMonth()
JavaScript 内置的函数来调用。
JavaScript 也对星期几进行同样的操作,从星期日开始。这样星期日就从 0 开始,星期一从 1 开始,以此类推。这是使用getDay()
JavaScript 中的函数调用的。
那么,问题就变成了我该如何使用这两条关键信息:1. 如何确定一个月的第一天是星期几?2. 如何计算一个月有多少天?
回答第一个问题很简单。JavaScript文献概述了内置函数的各种选项。神奇的是,new Date(year, month)
它功能强大,可以自动渲染给定年份和月份参数的第一天。使用.getDay()
这个函数,我就能获取月份第一天的日期索引。
接下来,我需要获取给定月份的天数。同样,我借助万能的函数new Date()
开始,并运用一些高中代数知识,逆向工程出了一种计算总天数的方法。该new Date()
函数可以接受大量参数,精确到毫秒。我不需要使用日历的精确度,但我认为可以使用月份、年份和日期参数。考虑到任何月份的最大天数是 31 天,输入 32 天new Date()
就能得到下个月的实际相对日期。例如,七月(年份的索引 6)当月有 31 天。如果我输入new Date(2019, 6, 32)
到控制台,我预计控制台会返回 2019 年 8 月 1 日。
该new Date()
函数还有一个getDate()
函数可以返回给定日期的天数。使用这个返回的数字,我将其从 32 中减去,就得到了给定月份的准确天数。
为了进一步证明我的概念,我在控制台中使用了 2019 年 2 月的数据。传入年份 2019、月份 1 和天数 32,我预计上述等式将返回 28,因为 2019 年 2 月有 28 天。
克服了一个障碍,现在我必须使用这些数据来填充和呈现我的表格。
填充野兽
从表格中删除硬编码的 HTML 数据后,我需要用当前渲染的月份和年份填充月份跨度和年份跨度。这相当简单,因为我把大部分 HTML 代码都留在了标题中。使用 找到各个跨度元素后document.findElementById()
,我将 更改textContent
为给定的月份和年份。
再次使用 获取表格元素后document.getElementById()
,我需要添加一些for
循环来渲染行和列。我还知道需要在日历上打印实际的日期。出于显而易见的原因,我将计数从 1 开始,并将 1 赋值给一个名为 的可用变量renderNum
。(注意:此变量会在每次列循环后加 1。稍后我会讲到这一点)
接下来,我生成了行for
循环,因为它将保存所有单独的日期图块。(更确切地说,我将在行后<tr>
附加表格数据<td>
元素。)由于我知道最多需要 6 行,所以我从传统的 0 开始循环,并在达到for
6 行时停止。由于该行没有文本内容,我只需要创建元素并将其赋值给一个可用的变量即可。<tr>
如前所述,我想在每一行中附加 7 个表格数据元素。这意味着我需要一个嵌套for
循环。第二个for
循环也从 0 开始,并在计数(在本例中为 c)达到 7 时结束。这很简单,但下一步需要一些逻辑推理。记住 给出new Date(month, year).getDay()
了星期几的索引,我需要设置空的<td>
s ,以便计数和日历渲染从正确的日期开始。为此,我if
在列for
循环中添加了一个语句,用于检查:1.循环是否位于第一行(又名i===0
),以及2.计数循环的值是否小于该new Date(month, year).getDay()
值(又名c < startOfMonth
)。
如果这两个条件都成立,我希望循环创建一个元素,向新渲染的元素<td>
添加一个空<td>
类,并将其附加到当前行元素。
接下来,我想确保在每个新的表格数据元素上填充正确的日期。我修改了空的 td 元素方法,<td>
为每个新的日期添加了一个元素,添加了textContent
,并附加了 行。textContent
在本例中, 是renderNum
之前定义的变量。为了确保renderNum
打印的日期正确,我将变量加 1。
利用真实性的力量,我使用了一个else if
语句来阻止日历在月份最后一天之后递增和添加数据。
一旦我到达break
并退出for
循环,我需要确保将新形成和填充的行附加到表主体中。(我确实根据变量和个人喜好重新排列和分组了代码。显然,这不是必需的。这只是个人喜好。)
最后,我添加了EventListeners
点击箭头切换到日历快照前后月份的功能。鉴于这篇文章已经很长了,我就不多说了。不过,如果你在这一步遇到困难,我推荐你阅读相关的文献。EventListeners
下面是日历渲染后的截图。项目完成后,我会更新这篇文章,并附上 GitHub 链接。
接下来是预约、时区和日程安排。祝我好运!
文章来源:https://dev.to/knheidorn/making-a-calendar-in-vanilla-javascript-48j8