)
文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载导读本文围绕 Modern JavaScript Tutorial 中「修改文档Modifying the document」章节的经典实战任务展开编写createCalendar(elem, year, month)函数用原生 JavaScript 动态生成指定年、月的日历表格并渲染到页面指定元素中。读完本文你将掌握用「HTML 字符串拼接 innerHTML」快速构建表格的套路理解 JavaScriptDate对象中「月份从 0 开始」和「getDay()以周日为一周起点」这两个极易踩坑的约定并收获一套可直接复制运行、可扩展为周历/月历组件的完整实现。任务要求生成一个纯展示的日历表格任务原文位于 task.md核心要求如下编写函数createCalendar(elem, year, month)调用该函数后应在elem内部生成指定year/month的日历日历是一个table一行一周用tr一天用td表头用th显示星期名一周从星期一开始依次排列到星期日示例createCalendar(cal, 2012, 9)应在元素cal中生成 2012 年 9 月的日历附加说明本任务只要求生成静态日历无需支持点击选择日期等交互。仓库为该任务同时提供了「初始模板」和「参考答案」两个可直接在浏览器中运行的页面分别位于 source.view/index.html 与 solution.view/index.html。初始模板已经内置了表格的边框样式border-collapse: collapse、单元格 1px 黑色边框、表头灰色背景加粗等并预留了createCalendar的空函数体我们要做的就是把函数体补全。整体思路字符串拼接 innerHTML在动手写循环之前先明确渲染策略。本任务所在的 07-modifying-document/article.md 章节讲解了多种修改文档的方式document.createElement、append/prepend/before/after、insertAdjacentHTML等。对于日历这种「结构化、批量生成」的表格最简洁的做法是用一个字符串变量把整个table.../table的 HTML 拼出来最后一次性通过elem.innerHTML table写入页面。这样只触发一次 DOM 解析与渲染代码也更易读、易调试。参考答案的算法步骤见 solution.md也正是如此生成表头th及星期名创建日期对象d new Date(year, month - 1)即该月 1 号注意月份索引从 0 开始用空的td/td填充 1 号之前、从周一到当天的空位逐日递增d.setDate(d.getDate() 1)只要d.getMonth()还在本月就追加td遇到周日一周末尾就追加换行标签/trtr月份结束后若最后一行不满 7 列补足空td让表格保持规整。难点一JavaScript 的月份索引从 0 开始new Date(year, month, day)中的month参数范围是0110 代表一月11 代表十二月而不是日常习惯的 112。因此「生成 2012 年 9 月」需要写成let mon month - 1; // 将用户传入的 1..12 转换为 Date 使用的 0..11 let d new Date(year, mon);此时d正好指向该月 1 号day缺省为 1。这一行是整个算法的锚点后续判断「是否还在本月」用d.getMonth() mon取日期数字用d.getDate()都建立在这个d之上。参考答案源码中特意用注释标注了这一点见 solution.view/index.html。难点二getDay() 以周日为起点需要转换Date.prototype.getDay()返回星期序号但它的约定是0 星期日1 星期一 …… 6 星期六而我们的日历要求「周一作为一周的第一天、周日作为最后一天」。直接使用getDay()会错位因此参考答案封装了一个辅助函数做映射function getDay(date) { // 返回 0(周一)..6(周日) let day date.getDay(); if (day 0) day 7; // 把周日(0)放到末尾 return day - 1; }转换逻辑先把周日的0变成7再统一减 1得到「周一 0、周二 1 …… 周日 6」的序列。这个辅助函数在三个地方都会被用到计算 1 号之前需要多少个空单元格即 1 号在周一视角下的序号判断当天是不是周日getDay(d) % 7 6决定是否换行判断最后一行还缺几个空单元格。提示也可以利用new Date(year, mon, 1)的getDay()原生值配合取模运算但显式的辅助函数语义更清晰便于维护。分步实现逐段拆解日历生成逻辑第 1 步生成表头表头是固定的七个th从MO周一到SU周日并与/tr一起拼入字符串同时开一个新的tr用于存放日期行let table tabletrthMO/ththTU/ththWE/ththTH/th thFR/ththSA/ththSU/th/trtr;第 2 步填充 1 号之前的空位如果 9 月 1 号是周四那么在周一的列、周二的列、周三的列上应该各放一个空单元格1 号才能正确落到周四列。循环次数正是getDay(d)的返回值// 视觉示意* * * 1 2 3 4* 为空格子1 为该月第一天 for (let i 0; i getDay(d); i) { table td/td; }第 3 步逐日生成日期单元格并处理换行进入主循环只要d还在本月就把日期数字写进td每遇到周日一周的最后一天就闭合当前行并开启新行随后把日期加一天while (d.getMonth() mon) { table td d.getDate() /td; if (getDay(d) % 7 6) { // 周日一周结束换行 table /trtr; } d.setDate(d.getDate() 1); }注意d.setDate(d.getDate() 1)的巧妙之处JavaScript 的Date会自动处理跨月与跨年例如 1 月 31 日加一天会变成 2 月 1 日、12 月 31 日加一天会变成次年 1 月 1 日因此主循环的终止条件只需比较getMonth()即可无需关心大月小月、平年闰年——Date已经帮我们全部算好了。第 4 步补齐最后一行循环结束时d已经指向下个月 1 号。如果它的「周一视角序号」不是 0说明最后一行还没满 7 列需要补空单元格让它成为完整的方形// 视觉示意29 30 31 * * * ** 为空格子 if (getDay(d) ! 0) { for (let i getDay(d); i 7; i) { table td/td; } }第 5 步闭合表格并写入页面最后拼上闭合标签并通过innerHTML一次性写入目标元素table /tr/table; elem.innerHTML table;完整参考实现将上述片段组合即得到完整的函数与仓库参考答案 solution.view/index.html 一致function createCalendar(elem, year, month) { let mon month - 1; // JavaScript 中月份是 0..11不是 1..12 let d new Date(year, mon); // 该月 1 号 // 表头周一 → 周日 let table tabletrthMO/ththTU/ththWE/ththTH/ththFR/ththSA/ththSU/th/trtr; // 1 号之前、从周一到当天的空位 // 视觉示意* * * 1 2 3 4 for (let i 0; i getDay(d); i) { table td/td; } // 逐日填充日期单元格 while (d.getMonth() mon) { table td d.getDate() /td; if (getDay(d) % 7 6) { // 周日是本周最后一天 → 换行 table /trtr; } d.setDate(d.getDate() 1); } // 最后一行不足 7 列时补空使表格方正 // 视觉示意29 30 31 * * * * if (getDay(d) ! 0) { for (let i getDay(d); i 7; i) { table td/td; } } // 闭合表格并写入页面 table /tr/table; elem.innerHTML table; } // 星期映射0(周一) .. 6(周日) function getDay(date) { let day date.getDay(); // 原生 getDay0 周日 if (day 0) day 7; // 把周日挪到一周末尾 return day - 1; } // 生成 2012 年 9 月9 月在 JS 中是第 8 个索引函数内部会做 -1 转换 createCalendar(calendar, 2012, 9);调用示例中的calendar是页面里预留的容器元素div idcalendar/div。由于该元素在script之前已存在直接用id即可在全局作用域引用更稳妥的写法是document.getElementById(calendar)。运行与验证方式仓库已提供可直接运行的页面无需构建工具初始模板source.view/index.html —— 含完整样式与空函数体适合自己动手补全参考答案solution.view/index.html —— 完整实现可直接对照。用浏览器直接打开上述任一 HTML 文件即可看到效果一个 7 列周一至周日、行数随月份天数变化的日历表格。建议用不同参数多测几个用例验证正确性2012 年 9 月该月 1 号是周六首行需 5 个空格子2012 年 2 月闰月29 天可验证Date的跨月进位2015 年 2 月平年 28 天某个月 1 号恰好是周一的年份首行无空格子第 2 步循环不执行。边界情况与易错点小结场景处理方式位置用户传入的月份是 112构造Date时减 1函数开头let mon month - 11 号不是周一首行先用空td占位第 2 步循环月末最后一天恰好是周六最后一行为完整 7 列无需补齐getDay(d) ! 0判断大月/小月/闰年交给Date自动进位循环只比较getMonth()主循环终止条件周日换行getDay(d) % 7 6判定主循环内部值得强调的还有一个常见的初级错误直接拿d.getDay()与6比较判断周日。原生getDay()中周日是0若不做映射周日就会被误判为「一周第一天」导致换行错位。务必统一使用映射后的getDay辅助函数。扩展思考从静态日历到可交互组件本任务刻意限定「只生成、不交互」但实现中已经埋下了向完整日历组件演进的伏笔标记今天/节假日在生成td时对满足d.getFullYear() year d.getMonth() mon d.getDate() today的单元格追加class或内联样式即可绑定点击事件生成后可用elem.querySelectorAll(td)收集日期单元格或用事件委托见本教程 2-events/03-event-delegation 一章在table上统一监听click切换月份把「当前显示的年月」提升为函数外的状态变量每次切换调用createCalendar重新渲染配合innerHTML的覆盖特性天然支持刷新其他渲染方案本任务用字符串拼接本章节还介绍了DocumentFragment与append(...nodes)的数组展开写法见 07-modifying-document/article.md当单元格内部结构复杂、需要以节点为单位操作时可以改用「创建节点 → 批量插入」的方式二者适用场景不同。总结createCalendar是「DOM 修改」章节中极具代表性的综合练习一次覆盖了三个关键知识点HTML 字符串拼接 innerHTML的批量渲染策略——结构化表格的首选方案Date对象的月份索引0 起与自动进位——写出健壮日期逻辑的前提getDay()的星期映射转换——实现「周一为一周起点」日历布局的关键细节。完整的任务说明见 task.md算法步骤见 solution.md可直接运行的模板与答案见上述两个index.html。把这份实现跑通并吃透边界逻辑你就掌握了用原生 JavaScript 从零生成日历类 UI 组件的基本功。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐JavaScript 日期创建实战本地时区与月份从 0 计数Modern JavaScript Tutorial 练习题解析JavaScript 日期创建实战本地时区与月份从 0 计数Modern JavaScript Tutorial 练习题解析 导读 本篇文章以《现代 Ja文档教程前端JavaScript 使用 Math.random() 生成 min 到 max 区间的随机浮点数Modern JavaScript Tutorial 实战JavaScript 使用 Math.random 生成 min 到 max 区间的随机浮点数Modern JavaScript Tutorial 实战 M文档/教程前端JavaScript 日期创建实战用 new Date 构造指定日期Modern JavaScript Tutorial 习题精解JavaScript 日期创建实战用 new Date 构造指定日期Modern JavaScript Tutorial 习题精解 导读 在 JavaSc文档/教程前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考