Web-Dev-For-Beginners Terrarium 项目实战:选一个 DOM 接口深入调研的“DOM 进阶作业”怎么做
本篇指南围绕 Web-Dev-For-Beginners 课程 Terrarium(生态瓶)项目的第三部分课后作业“再与 DOM 深入合作”展开:从 MDN 的 DOM 接口清单中挑选一个接口,找到真实网站中的使用案例,并写出带代码示例的使用说明。读完本文,你将完整拿到这份作业的原始要求与评分标准,了解本项目拖拽实现中已经用到的全部 DOM 接口作为调研起点,并能按可落地的三步路线(选题、调研、成文)交出一份符合评分要求的作业。
作业定位:它属于课程的哪个环节
Terrarium 项目分三课递进:第 1 课搭 HTML 骨架、第 2 课做 CSS 样式、第 3 课用原生 JavaScript 实现“把植物拖进瓶子”的交互。第 3 课的正文在 课程 README 中,结尾的 Assignment 小节直接指向本作业;仓库中的韩文版作业说明位于 assignment.ko.md,英文完整版位于 assignment.md。
这份作业的意义在于“从点到面”:你在课堂上刚刚亲手用过一小撮 DOM API(取元素、监听指针事件、改样式位置),而作业要求你跳出这一个项目里出现过的接口,到 MDN 的 DOM 接口清单中去“认领”一个更宽泛的接口,调研它在真实网站中是怎么被使用的,并写出自己的使用说明。这是从“会用几个 API”走向“理解整套 DOM 能力版图”的一步。
原始作业要求与评分标准(完整继承)
韩文版作业原文只有两个部分,要求非常明确,这里完整保留其内容:
说明(설명):认领(채택)一个 DOM 元素/接口,对 DOM 做进一步调研。访问 MDN 的 DOM 接口清单 页面并从中选择一个接口,然后在网上找到一个正在使用它的网站,针对该用法撰写一份说明文档。
评价标准(평가 기준):
| 기준(维度) | 模范答案 | 合适的答案 | 需要改进的答案 |
|---|---|---|---|
| (未单列维度名,按“成文质量”整体评估) | 以段落形式撰写,且带有示例 | 以段落形式撰写,但没有示例 | 没有任何记录 |
从这张表可以读出作业的核心判分点:答案必须是段落式的解释文字(而不是只贴一串代码或一堆链接),并且必须附带具体示例;只有“记录”而不带例子的答案会被降级。
英文完整版 assignment.md 对同一份作业给出了更细的五个维度评分细则,可以作为更严格的自查参照:
| 维度 | 模范(A) | 合格(B) | 发展中(C) | 需改进(D) |
|---|---|---|---|---|
| 技术理解 | 解释准确、术语规范,体现深入理解 | 理解扎实、大体准确 | 基础理解,存在少量误解 | 理解有限,错误明显 |
| 真实案例分析 | 找到并深入分析真实实现,有具体例证 | 找到真实案例,分析充分 | 找到了案例但分析浮于表面 | 现实关联含糊或不准 |
| 代码示例 | 可运行、注释完善、清晰演示接口 | 功能正确、注释充分 | 能跑但文档不足 | 代码有错误或解释混乱 |
| 写作质量 | 结构清晰、表达生动、技术沟通规范 | 组织良好 | 组织尚可 | 组织混乱或表达不清 |
| 批判性思考 | 能洞察概念间的联系并提出创新应用 | 分析到位、联系相关 | 有一些分析但不够深 | 批判性思考证据不足 |
调研起点:Terrarium 项目已经用过哪些 DOM 接口
做这份作业最省力的切入方式,是先盘点“自己项目里已经用到的 DOM 接口”,再向外圈扩展。从参考答案 script.js 的源码看,拖拽系统共动用了下面这些接口与属性,它们都天然适合作为你撰写调研文档时的对照组:
| 接口 / 属性 | 在代码中的位置 | 作用 |
|---|---|---|
document.getElementById() |
script.js L3-L16 连续 14 次调用 | 按 id 取回 plant1~plant14 元素 |
Element.onpointerdown |
script.js L29 terrariumElement.onpointerdown = pointerDrag; |
按下植物时触发拖拽起始 |
Event.preventDefault() |
script.js L32 | 阻止默认行为(如拖拽过程中选中文字) |
Event.clientX / Event.clientY |
script.js L35-L36、L46-L52 | 读取指针相对视口的坐标 |
document.onpointermove / onpointerup |
script.js L38-L40、L59-L63 | 挂在 document 上,保证指针移出元素后仍能持续追踪 |
Element.offsetTop / offsetLeft |
script.js L55-L56 | 读取元素相对定位祖先的当前位置 |
Element.style.top / style.left |
script.js L55-L56 | 用内联样式更新元素位置,实现“跟手” |
此外还有两个容易被忽略但同样属于 DOM 知识面的细节:
defer脚本加载策略:index.html L13 使用<script src="./script.js" defer></script>,确保 HTML 解析完成后脚本才执行,从而保证getElementById('plant1')这些调用执行时 14 个<img id="plantN">节点已经存在。- CSS 定位前提:
offsetTop/offsetLeft是相对“最近的定位祖先”计量的,而 style.css 中.plant声明了position: absolute、外层.plant-holder与.container均为绝对/相对定位——JavaScript 的拖拽数学只有在这套 CSS 前提成立时才正确。写调研文档时,这类“API 与样式层联动”的细节正是区分“合适答案”和“模范答案”的加分内容。
从 script.js L18-L64 的闭包结构还能看出一个设计模式:dragElement(terrariumElement) 每次被调用都会生成一组私有的 pos1~pos4 变量和三个内部函数(pointerDrag、elementDrag、stopElementDrag),14 株植物因此各自维护互不干扰的坐标状态。你的调研主题如果选到 classList、querySelector 等接口,可以顺带对比“本项目为何选择按 id 逐个取元素”而不是“用 querySelectorAll('.plant') 一次取全部”——这种对比本身就是英文作业模板里“Practical Application(与 terrarium 技术做对比)”部分要考察的内容。
作业完成的三步实操路线
以下路线依据英文版 assignment.md 的 Step 1~Step 3 整理,每一步都给出可直接照做的操作。
第一步:选一个 DOM 接口
打开 MDN 的 DOM 接口清单页面(即作业说明中给出的那个入口),从中挑一个。英文版作业按难度给了三档候选,可对照选择:
- 入门友好:
Element.classList(动态管理 CSS 类)、Document.querySelector()(进阶元素选取)、Element.addEventListener()(指针事件之外的事件处理)、Window.localStorage(客户端数据持久化) - 中级挑战:
Intersection Observer API(检测元素可见性)、MutationObserver(监听 DOM 变更)、Drag and Drop API(本项目指针方案之外的另一种拖拽路线)、Geolocation API(获取用户位置) - 进阶探索:
Web Components(自定义元素与 Shadow DOM)、Canvas API(程序化绘图)、Web Workers(后台线程)、Service Workers(离线能力)
选题建议遵循两条原则:一是选一个与 Terrarium 已用接口形成对照的(比如你用过 getElementById,就可以挑 querySelector 系来对比选取方式;你用过指针事件,就可以挑 Drag and Drop API 来对比拖拽实现路线);二是选一个你手头能立刻打开 DevTools 验证的,避免选到需要特殊硬件或权限的接口(如 Geolocation)导致无法给出可运行示例。
第二步:调研真实网站的实现
作业要求“在网上找到一个正在使用它接口的网站”,操作要点:
- 打开目标网站,按 F12 进入浏览器开发者工具;
- 在 Console 中用该接口做探测,例如
localStorage.getItem('某键名')看是否有返回、document.querySelectorAll('某选择器').length看选取规模,以此确认站点确实在使用这个能力; - 在 Network/Source 视图中搜索接口关键字(如
addEventListener、IntersectionObserver、localStorage),定位到具体是哪段代码在调用; - 记录三件事写进文档:开发者为什么选这个方案(例如用 IntersectionObserver 做懒加载是因为滚动监听开销大)、它如何改善用户体验(例如首屏图片延迟加载减少白屏)、以及你找到的代码片段。
第三步:撰写说明文档并附上可运行代码示例
英文版作业给出的提交结构模板如下,照此组织你的文档:
# [接口名] DOM 调研
## What It Does(技术概述)
用初学者能懂的语言定义该接口,说明它提供的关键方法/属性/事件,以及它要解决的问题
## Real-World Example(真实案例分析)
你找到的网站、具体实现细节(尽量附代码片段)、开发者选型动机、用户体验收益
## Code Demonstration(代码演示)
一个带注释、可运行、贴近真实场景的最小示例
## Reflection(反思)
该接口与 terrarium 项目技术的联系、如何扩展 terrarium、还有哪些项目适合用它
字数要求为 300~500 词;代码示例必须满足四个条件:功能可跑通、逐段有注释、与真实用例相关、初学者能读懂。写作上注意用“给朋友讲”的口吻而不是照抄文档,且每个技术结论尽量配一个具体例子——这正好对应韩文版评分表中“带示例的段落”这一模范答案标准。
下面给出一个贴合本项目结构的参考示例(localStorage 保存植物布局),可直接在 terrarium 的 script.js 中拖拽系统完成后追加测试:
// —— Window.localStorage 示例:记住用户摆放好的植物位置 ——
const STORAGE_KEY = 'terrarium-plant-positions';
// 页面加载时恢复:读取上次保存的布局并写回内联样式
const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
for (const id of Object.keys(saved)) {
const el = document.getElementById(id); // 与主程序相同的按 id 取元素方式
if (el) {
el.style.top = saved[id].top;
el.style.left = saved[id].left;
}
}
// 页面卸载前保存:遍历 14 株植物,记录当前 top/left
window.addEventListener('beforeunload', () => {
const positions = {};
for (let i = 1; i <= 14; i++) {
const el = document.getElementById('plant' + i);
if (el) positions['plant' + i] = { top: el.style.top, left: el.style.left };
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(positions));
});
验证方式:拖动几株植物后刷新页面,若布局保持原样即生效;在 DevTools 的 Application → Local Storage 中可看到 terrarium-plant-positions 键值。若你选的是 querySelector + classList 组合,也可以写更小的示例:
// 用 querySelectorAll 一次取回全部植物,点击切换高亮类
const plants = document.querySelectorAll('.plant');
plants.forEach((plant) => {
plant.addEventListener('click', () => plant.classList.toggle('highlighted'));
});
这个示例同时演示了“批量选取”与“动态类名管理”两个入门接口,适合作为作业中“入门友好”档位的演示素材。
对照评分表的交付前自查清单
交作业前,用韩文版评分标准逐条对照,避免掉进“没有记录”或“只有段落没有示例”两档:
- 是否为段落式说明:四个小标题下是否都是成段解释,而不是只贴 API 签名或代码?
- 是否带具体示例:文中是否至少有一个真实网站案例 + 一段可运行代码?(这是模范答案与合适答案的分界线)
- 接口名称与 MDN 是否一致:如
Intersection Observer的接口对象是IntersectionObserver,localStorage挂在Window下——术语错误在英文版评分表中直接拖累“技术理解”维度; - 代码是否真的测过:在浏览器里跑一遍,确认注释与实际行为相符;
- 是否建立了与 terrarium 的对照:Reflection 部分说明该接口如何扩展本项目(例如用 localStorage 保存布局、用 MutationObserver 监听植物增删),这是五维细则中“批判性思考”的得分点。
完成这份作业后,你对 DOM 的认知结构就从“我会拖拽”升级为“我知道 DOM 提供了哪些接口、它们各自解决什么问题、以及真实项目如何选型”——这正是从课程项目走向独立开发项目的关键一步。
仓库内延伸阅读路径:作业原文 assignment.ko.md、英文版 assignment.md、课程正文 README.md、参考实现 script.js、index.html、style.css。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

