3.4 DOM 修改:切除、移植与再造 别把修改 DOM 想成字符串拼接——直接拼 HTML 字符串再整体刷新,会把已经绑好的事件、挂在元素上的数据全部冲掉。正确的思路是把文档当成活体组织:新增的组织往哪个切口放进去、既有组织怎么整体移植、坏死部分如何切除,各有专门的术式。本节是本章的收官动作,也是第 6 章"AJAX 拿到数据往页面上放"的全部技术储备。 本节摘要:插入按方位分四刀: 与 进容器内部首尾, 与 贴在目标外侧前后;移植用 或组合 与 ;再造靠 (传真保留事件)与 ;切除三刀 、 、 按是否保留数据与事件区分。 插入四向:切口决定术式 四刀的分工看两个坐标:相对容器还是相对兄弟,在内部还是在外侧。记不清时用一句话校准——append 系找容器,after 系找邻居。
别把修改 DOM 想成字符串拼接——直接拼 HTML 字符串再整体刷新,会把已经绑好的事件、挂在元素上的数据全部冲掉。正确的思路是把文档当成活体组织:新增的组织往哪个切口放进去、既有组织怎么整体移植、坏死部分如何切除,各有专门的术式。本节是本章的收官动作,也是第 6 章"AJAX 拿到数据往页面上放"的全部技术储备。
本节摘要:插入按方位分四刀:
append与prepend进容器内部首尾,after与before贴在目标外侧前后;移植用appendTo或组合detach与append;再造靠clone(传真保留事件)与wrap;切除三刀remove、detach、empty按是否保留数据与事件区分。
// 容器内部:末尾追加 / 开头插入 $('#queue').append('<li class="item">新挂号 · 孙七</li>'); $('#queue').prepend('<li class="item">加急插队 · 周八</li>'); // 目标外侧:身后贴一个 / 身前贴一个 $('.urgent').after('<li class="note">候补一人</li>'); $('.urgent').before('<div class="divider">以下为急症</div>');
四刀的分工看两个坐标:相对容器还是相对兄弟,在内部还是在外侧。记不清时用一句话校准——append 系找容器,after 系找邻居。配套的主动语态版本 appendTo 适合链式场景:
$('<li class="item">新挂号 · 孙七</li>') .addClass('fresh') .appendTo('#queue'); // 先造好、穿好、再送进容器
既有元素搬家不需要拆了重建——把 jQuery 对象直接 append 进新容器,它会从原位置移动过去(文档里同一个节点只能有一个位置):
$('.urgent').appendTo('#done-list'); // 整体移植,事件与数据随行
需要"复制一份"时才请出 clone,参数决定手术质量:
var copy = $('.urgent').clone(); // 浅克隆:只有结构样式 var full = $('.urgent').clone(true); // 深克隆:连事件处理器一起复制
clone(true) 传真这个细节请先记住,它的意义要到第 4 章讲完事件绑定才能体会——没传真就搬家的元素,一到新地方就"点不动",是排错榜上的常客。外围手术还有 wrap(给目标套一层容器)与 replaceWith(以新换旧),按需查阅即可。
内容层面的批量替换走 html 与 text:html() 读写内部 HTML,text() 只碰纯文本。它们常被误用于高频刷新——每次 html() 都重新解析字符串并扔掉旧节点,频繁调用代价不小。
$('.done').remove(); // 摘除并销毁:数据与事件一并清除 $('.done').detach(); // 摘除但保留:还能原样接回 $('#queue').empty(); // 清空容器内部:容器自己留下
三刀的差别全在"切下来之后还认不认账"。remove 是彻底切除;detach 是暂存待回植——场景如"筛选期间先把不合条件的人摘出去,恢复时再接回来";empty 动的是容器内部,容器本身毫发无损。选错刀的典型症状是:用 remove 摘下来的元素接回去之后事件失灵——因为数据账和事件账在切除时已被销毁。

背景:分诊台叫到号后,该条目要从候诊队列移到"已完成"列表,并允许误操作撤销。操作、结果、解读、变式一次走完:
// 候诊与完成两个容器 var $queue = $('#queue'); var $done = $('#done-list'); // 叫号:找到队首,摘下(保留数据与事件),接进完成列表 function callNext() { var $first = $queue.children('.item').first(); if ($first.length === 0) { console.warn('队列已空'); return; } $first.removeClass('urgent stable').addClass('done'); $first.detach().appendTo($done); // detach 后接回,事件账未动 } // 撤销:从完成列表摘下最后一位,送回候诊队列开头 function undoLast() { var $last = $done.children('.item').last(); $last.detach().prependTo($queue); }
结果:条目在两个列表之间往返,类名随状态切换,绑在条目上的点击事件始终有效。解读:如果用的是 remove,第二次 callNext 之前接回的条目会变成"事件死体";detach 保住了整本账,流转才成立。变式:要求撤销时恢复原类名,就在摘下前先 data('prevClass', this.className) 存档、接回时还原——data 账本在这里恰好派上用场,呼应上一节。
| jQuery | 原生 | 备注 |
|---|---|---|
$box.append(html) |
box.insertAdjacentHTML('beforeend', html) 或先 createElement 再 appendChild |
原生 append 只收节点不收字符串 |
$el.after(x) |
el.insertAdjacentElement('afterend', x) |
现代原生已有 after、before |
$el.remove() |
el.remove() |
同名同义,已是标准 |
$el.replaceWith(x) |
el.replaceWith(x) |
同名同义 |
$el.detach() |
无对应 | 原生靠先摘下引用变通保留 |
$el.clone(true) |
el.cloneNode(true) |
但 cloneNode 不复制事件监听 |
注意最后一行:原生克隆从不带走监听器,jQuery 的 clone(true) 是类库自己记账才做到的——这是"事件账本存在类库里"的第一个信号,下一章就翻开这本账。
clone,且传真参数决定事件是否随行。remove 销毁、detach 暂存、empty 清壳。html(),每次解析字符串都是重新施工。after、remove、replaceWith 已成标准,简单场景可以直接迁移;detach 与带事件克隆是 jQuery 的独门账本。结构手术到此出师。下一章给这台手术台接神经:让页面听得懂点击、输入与提交。