主题
元素节点的添加/删除/替换/克隆与document 对象(Day13 3/3)
本页对应 Day13_DOM,共 5 段视频,约 50 分钟。
学习目标
- 能使用
document.createElement创建指定标签名的新元素,并为其设置文本或 HTML 内容。 - 能使用
appendChild将新元素追加为父元素的最后一个子节点,并使用insertBefore将新元素插入到指定子节点之前。 - 能使用
removeChild删除指定子元素,使用replaceChild用新元素替换指定旧子元素。 - 能使用
cloneNode(true/false)克隆元素,并说明参数对是否克隆子内容的影响,能解释同一元素在 DOM 中移动而非复制。 - 能使用
document.title读写网页标题,并结合setInterval与字符串截取、拼接实现标题栏滚动效果。 - 能列举并运用
document对象常用属性与方法,如documentElement、body、head、title、createElement、querySelector等完成页面元素操作。
知识点
先看页面结构:为什么需要元素节点增删改
待办清单的本质是:一个 ul 里放多个 li。用户点“添加”,我们希望动态多一个任务;点“删除”,删掉一个任务;点“替换”,把旧任务换成新任务;点“插入”,把新任务放到指定位置。为什么需要这些方法:只改 innerHTML 容易把已有结构和事件覆盖掉,而用 DOM 节点方法能精确操作某一个子节点。怎么想:先拿到父元素 ul、输入框和各个按钮,再创建/查找节点,最后由父元素调用添加、删除、替换方法。怎么做:准备如下结构,ID 按转写稿整理为 todoContent、input、addBtn、insertBtn、deleteBtn、replaceBtn。易错点:创建出来的元素只是“游离”在内存里,不调用添加方法不会出现在页面上。
html
<ul id="todoContent">
<li>吃饭</li>
<li>看电影</li>
</ul>
<input id="input">
<button id="addBtn">添加</button>
<button id="insertBtn">插入</button>
<button id="deleteBtn">删除</button>
<button id="replaceBtn">替换</button>js
const todoContent = document.querySelector('#todoContent');
const input = document.querySelector('#input');
const addBtn = document.querySelector('#addBtn');
const insertBtn = document.querySelector('#insertBtn');
const deleteBtn = document.querySelector('#deleteBtn');
const replaceBtn = document.querySelector('#replaceBtn');创建与追加:createElement + appendChild
为什么需要:不能每次都在 HTML 里手写新 li,要用 JS 动态创建。怎么想:document.createElement('li') 的参数是标签名字符串,返回值是新元素;新元素同样可以设置 innerHTML、样式和属性。怎么做:创建 li,把输入框的 value 写进去,再用父元素 todoContent.appendChild(newLi) 追加到末尾。易错点:转写稿中第一次点添加“没反应”,不是创建失败,而是新元素还没被加入文档;另外输入框内容用 input.value 获取,不是 innerHTML。
js
addBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = input.value;
todoContent.appendChild(newLi);
input.value = '';
};插入到指定位置:insertBefore
为什么需要:appendChild 只能追加到最后,但有时要把任务放到最前面。怎么想:仍然由父元素调用,insertBefore(新节点, 旧节点) 表示把新节点放到旧节点前面。怎么做:创建新 li 后,用 todoContent.insertBefore(newLi, todoContent.firstElementChild) 插到第一个子元素前。易错点:参数顺序是“新元素在前、旧元素在后”;方法名里没有 child,但它操作的仍是子节点。若再次把同一个 newLi 插入别处,它是“移动”,不是复制。
js
insertBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = input.value;
todoContent.insertBefore(newLi, todoContent.firstElementChild);
input.value = '';
};删除与替换:removeChild / replaceChild
为什么需要:清单需要删除最后一项,或把最后一项换成新内容。怎么想:删除和替换都由父元素调用;先找到要操作的那个子元素。怎么做:删除用 todoContent.removeChild(todoContent.lastElementChild);替换用 replaceChild(新元素, 旧元素)。易错点:删到没有子元素时,lastElementChild 为 null,再删会报错,实际项目应先判断;replaceChild 参数顺序不能反。
js
deleteBtn.onclick = function () {
const last = todoContent.lastElementChild;
if (last) todoContent.removeChild(last);
};
replaceBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = input.value;
const last = todoContent.lastElementChild;
if (last) todoContent.replaceChild(newLi, last);
input.value = '';
};克隆:cloneNode 与本尊/复制品
为什么需要:如果反复 appendChild(newLi),同一个节点只会被移动,页面上始终只有一个。怎么想:cloneNode(true) 返回克隆后的元素,true 表示连内部文本一起克隆;不传或传 false 只克隆元素本身。怎么做:添加克隆版,而不是本尊。易错点:不写 true 时,新 li 里没有文字;克隆出来的是新节点,可以多次添加,本尊不动。
js
addBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = '我是新元素' + Math.random();
todoContent.appendChild(newLi.cloneNode(true));
};补充:document.title 与标题滚动
为什么需要:标题栏空间有限,长标题可做滚动提示。怎么想:标题不能用 scrollLeft,但可以不断改 document.title 的字符串顺序。怎么做:每次取第一个字符拼到最后,定时器重复执行。易错点:变量名或 slice 拼错时,控制台会出现 undefined 相关报错,先看控制台再改代码。
js
document.title = '你有一条新消息:澳门最大的线上赌场上线了';
setInterval(function () {
const t = document.title;
document.title = t.slice(1) + t[0];
}, 300);document 是什么
document 表示整个文档,是 window 的一个属性;它也是 HTML 元素的父节点。页面里的元素、标题、cookie 等,都可以通过 document 来读写。
为什么需要:JS 要操作页面,先要有入口。document 就是这个入口。
怎么想:把 document 看成“整个文档对象”,先通过它拿到元素,再操作元素。
怎么做:直接使用 document.xxx 或 document.方法()。
易错点:document 不是某个具体标签,而是文档对象;documentElement 才是 HTML 根元素。
笔记中列出的属性和方法要保留:
js
documentElement // 获取 html 根元素
body // 获取 body 元素
head // 获取 head 元素
all // 获取所有元素组成的集合
title // 读写标题栏标题
cookie // 读写 cookie 信息
createElement()
getElementById()
getElementsByTagName()
getElementsByClassName()
getElementsByName()
querySelector()
querySelectorAll()获取元素与创建元素
为什么需要:要做“待办清单”的添加、插入、删除、替换,先要拿到按钮、输入框、列表等元素;要新增任务,还要创建新的 li。
怎么想:先获取已有元素,再用 document.createElement('li') 创建新元素。创建出来的元素和查询得到的元素本质一样,都可以设置样式、属性、内容。
怎么做:
js
const todoContent = document.querySelector('选择UL的选择器');
const addBtn = document.querySelector('选择添加按钮的选择器');
const inputBox = document.querySelector('选择输入框的选择器');
addBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = inputBox.value;
todoContent.appendChild(newLi);
inputBox.value = '';
};易错点:只 createElement 并设置内容,元素还“游离”在页面结构之外,不会显示;必须用 appendChild 等方法加入文档。转写稿里第一次点添加没反应,原因就是新 li 还没被添加到 ul 中。
添加与插入子节点
为什么需要:新任务默认追加到最后;有时又要插到最前面或指定元素前面。
怎么想:添加子节点由父元素调用。追加用 appendChild;插入到某个旧元素前面用 insertBefore(新元素, 旧元素)。
怎么做:
js
todoContent.appendChild(newLi);
todoContent.insertBefore(newLi, todoContent.firstElementChild);insertBefore 虽然名字里没有 append,但功能也是添加子节点,参数顺序是“新元素在前,旧元素在后”。
易错点:不要写成子元素调用;插入位置要传父元素下已存在的子元素。转写稿中把新元素插到“吃饭”前面,就是用第一个子元素作为参照。
删除与替换子节点
为什么需要:任务会完成,也会写错,需要删除或替换。
怎么想:删除、替换都由父元素调用。删除用 removeChild(要删的元素);替换用 replaceChild(新元素, 旧元素)。
怎么做:
js
todoContent.removeChild(todoContent.lastElementChild);
const newLi = document.createElement('li');
newLi.innerHTML = inputBox.value;
todoContent.replaceChild(newLi, todoContent.lastElementChild);易错点:删除前要先找到要删的元素;列表删空后继续删最后一个会报错。转写稿演示中删到没有最后一项就报错,实际开发可先判断是否存在子元素。
克隆元素
为什么需要:同一个新元素反复添加时,如果直接添加本尊,它会在页面中“挪位置”,不是复制。
怎么想:要保留原元素,就克隆一份再添加。cloneNode(true) 返回克隆后的元素,参数 true 表示连同内部内容一起克隆。
怎么做:
js
const copy = newLi.cloneNode(true);
todoContent.appendChild(copy);易错点:不传 true 时只克隆元素本身,不克隆里面的文字内容。转写稿中反复点添加、插入、替换,发现总是同一个元素在移动,因为变量始终指向同一个新元素;添加克隆版后,本尊不动,复制品才会不断增加。
title 标题滚动案例
为什么需要:标题栏空间有限,长标题显示不全,可以用 document.title 做滚动提示。
怎么想:标题是字符串。每次取出第一个字符拼到末尾,再写回 document.title,定时器不断执行,就像向左滚动。
怎么做:
js
document.title = '你有一条新消息:澳门最大的线上赌场上线了';
setInterval(function () {
const first = document.title.charAt(0);
const rest = document.title.slice(1);
document.title = rest + first;
}, 300);易错点:charAt 拼错会得到 undefined,再调用 slice 就报错;scrollLeft 不能直接让标题栏滚动。转写稿调试时正是 charAt 写错导致控制台报错,改正后标题就能滚动。
本节说明
本节笔记的作业是:
- 随机点名器
- 选项卡效果
- 优化图片延迟加载
- 实现图片无缝滚动
课堂转写稿没有展开这四个作业的具体实现,而是围绕 to-do list 示例讲了 DOM 元素节点的创建、添加、删除、替换、克隆,以及用 document.title 做标题栏滚动。下面按转写稿整理本节核心知识;作业四项需要综合运用这些节点操作完成,不能凭空编造课堂未讲的内容。
创建新元素并追加:为什么需要 → 怎么想 → 怎么做 → 易错点
为什么需要:页面上原来的任务列表是写死的,用户点“添加”时,应该动态生成新的 li,而不是手写 HTML。
怎么想:先拿到父元素 ul、按钮和输入框;点击按钮时创建 li,把输入框内容写进去,再追加到 ul 末尾。
怎么做:
js
const toDoContent = document.querySelector('#toDoContent');
const addBtn = document.querySelector('#addBtn');
const inputBox = document.querySelector('#input');
addBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = inputBox.value;
toDoContent.appendChild(newLi);
inputBox.value = '';
};document.createElement('li') 的参数是标签名字符串,返回值是新元素。新元素刚创建时“游离在三界之外”,还没有进入页面结构;必须用父元素调用 appendChild(newLi) 追加到最后。
易错点:只创建不追加,页面不会变化;appendChild 是父元素调用;输入框内容用 inputBox.value 读取;追加后可清空输入框,转写稿也提醒空值可自行加判断。
插入到指定位置:为什么需要 → 怎么想 → 怎么做 → 易错点
为什么需要:appendChild 只能追加到最后,但有时要把新任务插到最前面。
怎么想:仍然先创建 li,再用 insertBefore(newNode, oldNode),把新节点放到某个已有子节点前面。
怎么做:
js
insertBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = inputBox.value;
toDoContent.insertBefore(newLi, toDoContent.firstElementChild);
inputBox.value = '';
};易错点:insertBefore 也是父元素调用;第二个参数必须是该父元素已有的子元素;方法名里没有 child,但它同样是添加子节点。转写稿中强调:想插到“吃饭”前面,就把“吃饭”作为第二个参数。
删除与替换:为什么需要 → 怎么想 → 怎么做 → 易错点
为什么需要:任务清单要能删掉一项,也要能把某一项换成新内容。
怎么想:删除用父元素 removeChild(要删的元素);替换用父元素 replaceChild(新元素, 旧元素)。
怎么做:
js
deleteBtn.onclick = function () {
toDoContent.removeChild(toDoContent.lastElementChild);
};
replaceBtn.onclick = function () {
const newLi = document.createElement('li');
newLi.innerHTML = inputBox.value;
toDoContent.replaceChild(newLi, toDoContent.lastElementChild);
};易错点:删除前要先找到要删的元素;没有子元素时继续删会报错,转写稿说可自行加判断;replaceChild 参数顺序是“新在前、旧在后”。
克隆节点:为什么需要 → 怎么想 → 怎么做 → 易错点
为什么需要:同一个新元素如果反复 appendChild、insertBefore、replaceChild,它不是复制,而是“搬家”,旧位置就没有了。转写稿演示:代码写在外面只创建一次,变量始终指向同一个元素,所以点添加、插入、替换时感觉“怎么还是它”。
怎么想:要保留本尊不动,就克隆一份再添加。
怎么做:
js
const copy = newLi.cloneNode(true);
toDoContent.appendChild(copy);cloneNode(true) 表示元素和里面内容一起克隆;不写参数或写 false 只克隆元素本身,不带内容。
易错点:克隆后仍要追加;不加 true 会丢内容;本尊和复制品是两个节点。
document.title 与标题滚动:为什么需要 → 怎么想 → 怎么做 → 易错点
为什么需要:标题栏空间有限,长标题显示不全,可以做成滚动效果。标题栏不能用 scrollLeft,因为它是特殊区域。
怎么想:把标题字符串的第一个字符取出来拼到最后,定时器反复执行,视觉上就像向左滚动。
怎么做:
js
setInterval(function () {
const title = document.title;
document.title = title.slice(1) + title.charAt(0);
}, 300);易错点:slice 拼错会得到 undefined,再调用方法就报错;charAt(0) 取首字符;速度用时间间隔调整。转写稿调试时就是 slice 拼写错误导致后台报错,改正后滚动正常。
作业说明
作业四项——随机点名器、选项卡效果、图片延迟加载、图片无缝滚动——需要在本节 DOM 节点操作基础上完成。转写稿没有给出这四项的具体代码,因此这里只保留作业清单,不编造实现步骤。
课堂画面





代码示例
一、To Do List:元素节点的添加 / 插入 / 删除 / 替换 / 克隆(完整可运行)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>To Do List</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; padding: 20px; }
input { width: 200px; height: 28px; padding: 0 8px; }
button { height: 32px; margin-right: 4px; cursor: pointer; }
ul { margin-top: 16px; }
li { line-height: 2; }
</style>
</head>
<body>
<h2>我今天计划做的事情</h2>
<input type="text" id="input" placeholder="请输入任务">
<button id="addBtn">添加</button>
<button id="insertBtn">插入到最前</button>
<button id="deleteBtn">删除最后一项</button>
<button id="replaceBtn">替换最后一项</button>
<button id="cloneBtn">克隆第一项</button>
<ul id="todoContent">
<li>喝茶</li>
<li>钓鱼</li>
<li>看电影</li>
<li>吃晚饭</li>
</ul>
<script>
// 1. 先获取相关元素
var todoContent = document.querySelector('#todoContent');
var inputBox = document.querySelector('#input');
var addBtn = document.querySelector('#addBtn');
var insertBtn = document.querySelector('#insertBtn');
var deleteBtn = document.querySelector('#deleteBtn');
var replaceBtn = document.querySelector('#replaceBtn');
var cloneBtn = document.querySelector('#cloneBtn');
// 2. 添加:创建新元素 → 设置内容 → 追加到 UL 最后
addBtn.onclick = function () {
if (inputBox.value.trim() === '') return; // 空值不执行
var newLi = document.createElement('li'); // 参数是标签名字符串
newLi.innerHTML = inputBox.value; // 新元素同样有 innerHTML
todoContent.appendChild(newLi); // 必须是父元素调用
inputBox.value = ''; // 清空输入框
};
// 3. 插入:insertBefore(新元素, 旧元素) —— 插到旧元素前面
insertBtn.onclick = function () {
if (inputBox.value.trim() === '') return;
var newLi = document.createElement('li');
newLi.innerHTML = inputBox.value;
todoContent.insertBefore(newLi, todoContent.firstElementChild);
inputBox.value = '';
};
// 4. 删除:父元素.removeChild(要删的子元素)
deleteBtn.onclick = function () {
if (todoContent.lastElementChild) { // 删空了要判断,否则报错
todoContent.removeChild(todoContent.lastElementChild);
}
};
// 5. 替换:父元素.replaceChild(新元素, 旧元素)
replaceBtn.onclick = function () {
var newLi = document.createElement('li');
newLi.innerHTML = '我是替换出来的新任务 ' + Math.random().toFixed(2);
if (todoContent.lastElementChild) {
todoContent.replaceChild(newLi, todoContent.lastElementChild);
}
};
// 6. 克隆:cloneNode(true) 连内容一起克隆,本尊岿然不动
cloneBtn.onclick = function () {
var first = todoContent.firstElementChild;
if (!first) return;
var copy = first.cloneNode(true); // 不加 true 只有空标签
todoContent.appendChild(copy);
};
</script>
</body>
</html>关键行与易错点
document.createElement('li'):参数是标签名字符串,不要写成'<li>';创建出来的元素和querySelector拿到的元素完全一样,样式、属性、innerHTML都能操作。appendChild/insertBefore/removeChild/replaceChild都由父元素调用,参数是子元素。insertBefore名字里没有 "child",但它同样是添加子节点。insertBefore(newLi, oldLi):第二个参数是已存在的子元素,新元素会插到它前面;想插到最前面就传firstElementChild。- 用
lastElementChild/firstElementChild而不是lastChild/firstChild,后者会把换行文本节点也算进去。 cloneNode(true)的true表示连子内容一起克隆;不传只有空标签。克隆出来的才是"复制品",直接appendChild本尊是把本尊挪走,原位置就没有了。- 输入框为空时先
return,否则会添加空li;删除、替换前也要判断元素是否存在,否则removeChild(null)直接报错。
二、document.title 实现标题栏滚动(完整可运行)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>标题滚动</title>
</head>
<body>
<h2>请看浏览器标签页上的标题,它一直在滚动</h2>
<script>
// document.title 是读写标题栏的快捷方式
document.title = '你有一条新消息:澳门最大的线上赌场上线了,性感荷官在线发牌';
setInterval(function () {
var t = document.title; // 拿到当前标题字符串
document.title = t.slice(1) + t.charAt(0); // 第一个字符挪到最后
}, 300);
</script>
</body>
</html>关键行与易错点
- 标题栏在
<head>里,不能用scrollLeft让它滚动,只能不停改写document.title的文字内容,靠"截头补尾"制造向左滚动的错觉。 t.slice(1)取从第 2 个字符开始的剩余部分,t.charAt(0)取第一个字符,两者拼接后赋值回document.title。- 方法名拼写必须准确:写成
sclie之类,t就是undefined,undefined.slice会直接抛错,定时器里刷屏报错。 - 间隔时间(这里是 300ms)越小滚得越快,100ms 会明显一卡一卡;不要忘记定时器会一直执行,循环滚动永不停止。
- 没有
document.title这种快捷方式时,得先document.querySelector('title')再改innerHTML/innerText。
三、document 对象的常用属性与方法(完整可运行)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>document 对象</title>
</head>
<body>
<ul id="list">
<li class="item">喝茶</li>
<li class="item">钓鱼</li>
</ul>
<p id="out"></p>
<script>
// 属性
console.log(document.documentElement); // <html> 根元素
console.log(document.head); // <head>
console.log(document.body); // <body>
console.log(document.all.length); // 所有元素组成的集合
console.log(document.title); // 读标题
document.title = '标题被我改了'; // 写标题
// 方法
console.log(document.getElementById('list'));
console.log(document.getElementsByTagName('li'));
console.log(document.getElementsByClassName('item'));
console.log(document.querySelector('#list'));
console.log(document.querySelectorAll('li'));
// 演示:document 是 html 元素的父节点,window.document 就是它
document.getElementById('out').innerHTML =
'根元素:' + document.documentElement.tagName +
',body 子元素个数:' + document.body.children.length;
</script>
</body>
</html>关键行与易错点
document表示整个文档,是html元素的父节点,同时是window的一个属性,所以window.document === document。documentElement拿的是<html>,body/head直接拿对应元素,比querySelector更短。document.all是所有元素组成的集合,document.title是标题的读写快捷方式;cookie用来读写登录等信息,要到前后端交互阶段才用得上。getElementsByTagName/getElementsByClassName/getElementsByName返回的是动态集合,querySelectorAll返回的是静态 NodeList,后者更常用。- 取 URL、域名这类信息推荐用
location,不要用document.URL/document.domain这类基本用不上的属性。
常见错误
1. 创建了元素,页面却毫无反应
- 现象:
document.createElement('li')执行成功,控制台能看到对象,但页面上什么都没多出来。 - 原因:创建只是让节点存在于内存中(游离节点),从未挂到文档树上;漏写了
appendChild/insertBefore/replaceChild。 - 修法:把“创建 + 设置内容 + 插入”当成一个固定三步动作,写完
createElement立刻补上插入语句,并在插入后刷新验证。
2. 回调函数里创建的元素写在回调外面,反复点击只出现一次
- 现象:第一次点“添加”有效,后面再点按钮,节点只是从原位置“跑”到新位置,数量始终是 1。
- 原因:
createElement写在了onclick外面,全程只执行一次,变量始终指向同一个节点;DOM 中同一节点只能存在于一个位置,再次插入等于移动。 - 修法:需要多次生成就把创建语句放进事件回调内部;需要在多个位置复用同一份内容就用
cloneNode(true)。
3. cloneNode() 没带参数,克隆出来的是空壳
- 现象:克隆出的
li里文字、子元素全没了,只剩标签本身。 - 原因:
cloneNode()默认浅克隆,只复制元素节点本身,不复制子节点和文本。 - 修法:需要连内容一起复制就写
cloneNode(true);只有确实想要空容器时才省略参数。
4. replaceChild / insertBefore 的参数顺序写反
- 现象:控制台报
NotFoundError: Failed to execute 'replaceChild' on 'Node',或替换后原节点还在。 - 原因:两个方法都是“新节点在前、参照/被替换的旧节点在后”,习惯性按中文语序写成“旧→新”就会错;
insertBefore的第二个参数还必须是父元素的直接子节点,传null或不相关的节点也会报错。 - 修法:记成“新替旧、新插旧前”,第二个参数统一用
xxxElementChild这类从父元素上取到的真实子节点。
5. 删到最后一个就报错
- 现象:连点删除,删完后控制台抛
TypeError: Cannot read properties of null。 - 原因:
lastElementChild在子元素清空后返回null,直接把它传给removeChild就会崩。 - 修法:删除前判断
if (父元素.lastElementChild) { ... },或改用lastElementChild?.remove()。
6. 用 lastChild 代替 lastElementChild,删除/替换行为诡异
- 现象:有时删掉的是空白,有时一次删两个,
replaceChild报错。 - 原因:
lastChild/childNodes会把标签之间的换行、空格当成文本节点,取到的不是你以为的那个元素。 - 修法:只要操作元素,一律用
children/firstElementChild/lastElementChild。
7. 标题滚动案例里字符串方法写错
- 现象:定时器一启动就报
... is not a function,标题不动。 - 原因:把
slice写成splice、concat拼错成conect,导致取到undefined再调用方法。 - 修法:报错先看行号定位到具体调用;拼接优先用模板字符串
`${rest}${first}`代替+,能少踩一半拼写坑。
练习
1. 待办清单完整版 基于课堂的 to-do list,补齐四个按钮:追加到末尾、插入到第一项之前、删除最后一项、把最后一项替换成新输入内容。要求:输入为空时点击任何按钮都不产生空条目;每次操作后清空输入框并自动聚焦。
2. 用克隆做“再来一份” 页面上有一个带图标、带子 span 的复杂卡片。点击按钮时,在列表末尾追加一份完全一样的卡片,并给克隆出来的卡片标题加上“副本 2、副本 3”的序号。要求原卡片始终留在原位不被移动。
3. 复制已有节点 vs 移动已有节点 页面上放两个 ul,第一个 ul 里已有 3 个 li。做两个按钮:一个把第一个 ul 的第一个 li 挪到第二个 ul,另一个把它的副本放过去。观察并口头解释两者在数量上的差别。
4. 标题栏滚动增强版 实现 document.title 滚动,并增加控制能力:点击“暂停/继续”切换滚动状态,点击“重置”恢复初始文本。注意 clearInterval 后变量要置空,避免重复启动产生多个定时器。
5. 安全删除封装 写一个 removeLast(parent) 函数,父元素没有子元素时不做任何事也不报错,有子元素时删掉最后一个并返回被删掉的节点。再用它连续删 5 次做验证。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 创建新元素和添加子元素 | 17:18 |
| 2 | 删除子元素 | 03:02 |
| 3 | 替换子元素 | 03:38 |
| 4 | 克隆元素 | 14:44 |
| 5 | 标题栏滚动案例 | 11:46 |
小结
- DOM 操作永远是“两步走”:先在内存里造/找节点,再显式挂到树上;只做第一步,页面一定不动。
- 节点是对象引用,不是值拷贝——同一个节点在文档中永远只有一份,重复插入等于搬家,不会变多。想变多只有
cloneNode(true)。 - 创建语句放在回调里还是回调外,决定了“点一次加一个”还是“点一百次也就一个”,这是初学者最常踩的坑,写完先连点三次验证。
- 增、删、替三个方法都由父元素调用,参数里出现的一定是子节点;
insertBefore和replaceChild的第二个参数负责“定位/被替换”,写错顺序或传入非子节点必报错。 - 取子元素一律用带
Element的那一套(children/firstElementChild/lastElementChild),避免被空白文本节点干扰。 - 所有删除、替换、插入都要考虑边界:空列表、只剩一个、目标不存在。加上一行判空,代码就从不稳定变成稳定。
document上的大部分属性(URL、domain、lastModified等)实际项目里用location等更专门的 API 代替;真正高频的是createElement系列方法和title、cookie。- 标题栏是特殊区域,无法用 CSS 滚动,只能靠定时器改写字符串顺序来“伪造”滚动效果——这类“换思路绕过限制”的做法,比死磕某个 API 更有价值。