文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载导读在 Modifying the document 这一章中教程通过一系列任务引导读者掌握 DOM 动态生成技术。createCalendar(elem, year, month)月历生成任务位于 9-calendar-table正是其中最具代表性的综合练习之一它要求仅用innerHTML字符串拼接在任意容器元素中渲染出完整的月历表格。读完本文你将掌握先把整张表格拼成字符串、再一次性写入 DOM的高效思路理解 JavaScript 日期对象月份从 0 计数的坑以及如何把周日从getDay()的 0 转换为周序中的最后一天从而写出可直接复用的月历生成函数。任务定义函数签名与预期效果根据任务描述见 task.md需要实现function createCalendar(elem, year, month) { ... }调用createCalendar(cal, 2012, 9)后应在元素cal中生成 2012 年 9 月的日历表格。其结构要求为一周对应一个tr一天对应一个td表头使用th从周一开始、以周日结束MO、TU、WE、TH、FR、SA、SU该月第一天之前的格子留空最后一行不足 7 列时同样用空格补足任务只要求生成日历不要求日期可点击后续章节才会引入交互。仓库同时提供了任务骨架页 source.view/index.html 与完整解答页 solution.view/index.html前者只包含空函数骨架和样式后者给出了最终实现可对照学习。核心思路把表格拼成字符串再一次性交给 innerHTML官方解答solution.md开门见山地给出了解题方向先将整张表格构建为一个字符串table.../table最后赋值给elem.innerHTML。这与 07-modifying-document/article.md 中介绍innerHTML时的用法一脉相承先构造好 HTML 内容再写入属性。相比逐格createElementappend字符串拼接的优势是代码更短、更直观表格结构在字符串里一目了然只需一次 DOM 写入性能开销更小便于先在内存中验证拼接结果再统一渲染。需要留意的是本任务写的是静态字符串不含用户输入因此不存在 HTML 转义注入问题但教程在 article.md 中强调若内容来自用户输入使用insertAdjacentHTML或innerHTML时必须先转义、等字符否则可能引发 XSS 风险。算法五步拆解官方解答把整个生成过程归纳为五步solution.md第 1 步构造表头th先输出表头行let table tabletrthMO/ththTU/ththWE/ththTH/ththFR/ththSA/ththSU/th/trtr;这一行同时完成了table、第一行的tr、7 个星期表头以及第二行的tr开头——因为下一行紧接着就要放日期格子。字符串以tr收尾意味着后续拼接的td都会进入第二行。第 2 步构造日期对象锁定当月第一天let mon month - 1; // months in JS are 0..11, not 1..12 let d new Date(year, mon);这是 JavaScript 日期最经典的坑月份从 0 开始new Date(2012, 8)才是 2012 年 9 月。所以对外接口接收自然月month112内部必须做month - 1转换。此时d恰好是该月 1 号。第 3 步补足 1 号之前的空格用d.getDay()判断 1 号落在星期几在其之前填充空tdfor (let i 0; i getDay(d); i) { table td/td; }这里使用的getDay(date)是自定义辅助函数详见下文周一为首的关键转换它把返回值归一化为 0周一到 6周日因此循环次数正好等于周一到 1 号之间缺几格。第 4 步逐日推进遇到周日换行这是核心循环。只要d仍在本月就输出一个日期格子并在周日时追加/trtr结束当前行while (d.getMonth() mon) { table td d.getDate() /td; if (getDay(d) % 7 6) { // sunday, last day of week - newline table /trtr; } d.setDate(d.getDate() 1); }关键点d.getMonth() mon是循环终止条件d.setDate(d.getDate() 1)逐日加 1一旦跨入下个月getMonth()就不再等于mon循环自然结束判断是否周日getDay(d) % 7 6即归一化后的周序号为 6周日换行通过追加闭合与开启标签实现/trtr巧妙地在一个字符串中完成行收尾与下一行开启。由于setDate支持跨月/跨年溢出例如 9 月 30 日加 1 会自动变成 10 月 1 日所以无需手动处理每月天数这一写法对 28、29、30、31 天的月份全部通用。第 5 步补足末行闭合表格本月结束后最后一行可能不满 7 格用空格补成正方形然后闭合if (getDay(d) ! 0) { for (let i getDay(d); i 7; i) { table td/td; } } table /tr/table;注意循环退出时d已经指向下月 1 号getDay(d)恰好等于本月底最后一天在周中的序号 1若为 0 则说明月末正好是周日、行已满无需补格。这正是用下月 1 号的星期信息反推本月末尾空缺的精妙之处。最后elem.innerHTML table;一次性写入。周一为首的关键转换getDay 辅助函数浏览器原生date.getDay()返回 0周日 6周六而本任务要求周一是首列。解答用一个辅助函数完成映射function getDay(date) { // get day number from 0 (monday) to 6 (sunday) let day date.getDay(); if (day 0) day 7; // make Sunday (0) the last day return day - 1; }两步变换先把周日的 0 改成 7挪到末尾再整体减 1最终得到周一 0、周日 6的周序号。这样第 3 步的for (let i 0; i getDay(d); i)能准确填出 1 号前的空格数第 4 步的getDay(d) % 7 6能精确命中周日第 5 步的getDay(d)能算出末行剩余格数。完整可运行代码将以下代码放入 solution.view/index.html 所示的页面骨架含#calendar容器与基础表格样式即可运行div idcalendar/div script function createCalendar(elem, year, month) { let mon month - 1; // months in JS are 0..11, not 1..12 let d new Date(year, mon); let table tabletrthMO/ththTU/ththWE/ththTH/ththFR/ththSA/ththSU/th/trtr; // spaces for the first row // from Monday till the first day of the month // * * * 1 2 3 4 for (let i 0; i getDay(d); i) { table td/td; } // td with actual dates while (d.getMonth() mon) { table td d.getDate() /td; if (getDay(d) % 7 6) { // sunday, last day of week - newline table /trtr; } d.setDate(d.getDate() 1); } // add spaces after last days of month for the last row // 29 30 31 * * * * if (getDay(d) ! 0) { for (let i getDay(d); i 7; i) { table td/td; } } // close the table table /tr/table; elem.innerHTML table; } function getDay(date) { // get day number from 0 (monday) to 6 (sunday) let day date.getDay(); if (day 0) day 7; // make Sunday (0) the last day return day - 1; } createCalendar(calendar, 2012, 9); /script运行后2012 年 9 月的日历会以 7 列 x 5 行的规整表格呈现9 月 1 日恰好是周六第 6 列因此首行有 5 个空格、随后是 1、2 两天月末 9 月 30 日是周日整月刚好在周行结束末行无需补格。知识点延伸与本任务在章节中的定位本任务属于 07-modifying-document 章的练习题之一同章还有 6-create-list、7-create-object-tree、12-sort-table 等任务共同训练字符串/节点两种构建路径。本任务体现的能力点包括innerHTML批量写入相比逐节点append用字符串拼表更贴合整块内容一次性渲染的场景Date的月份 0 基制与setDate溢出机制自动处理了不同月份的天数差异用getDay()的归一化判断星期将原生 0周日语义转换为周一为首的周序号模板化行尾处理/trtr字符串技巧把换行压缩为一次拼接是此类表格生成题的高频解法。需要说明的是本解答基于仓库中 solution.md 的官方算法与 solution.view/index.html 的完整实现整理而成读者可直接在本地打开solution.view/index.html查看运行效果或在source.view/index.html骨架中自行实现后再对照答案。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐现代 JavaScript 教程实战实现 ucFirst 首字母大写函数——字符串不可变性与拼接方案现代 JavaScript 教程实战实现 ucFirst 首字母大写函数——字符串不可变性与拼接方案 ucFirst str 是《现代 JavaScript文档教程前端freeCodeCamp 基础 JavaScript 精讲用 运算符Plus Equals Operator拼接字符串freeCodeCamp 基础 JavaScript 精讲用 运算符Plus Equals Operator拼接字符串 本文深度剖析 freeCod前端后端教育IsaacLab Docker 部署完整指南预构建镜像 30 秒跑通容器化机器人学习环境IsaacLab Docker 部署完整指南预构建镜像 30 秒跑通容器化机器人学习环境 装一套 IsaacLab 机器人学习环境往往要手动对齐 Isaac人工智能强化学习机器人具身智能深度学习创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考