写网页如搭积木:光会摆弄现成的还不够,得学会自己造积木、插积木、拆积木。 这一式,我们修炼节点的增删改——让页面像活水一样流动。

本式要点
  • 用 $('<li>') 凭空造出元素,并用 text / attr 妆点它
  • 掌握 append / prepend / after / before 四个安放方位
  • 分清 remove() 与 empty(),知道 detach() 留了一手
  • 用 clone(true) 连事件一起复制
  • 会用 wrap / wrapAll / wrapInner / unwrap 包装与拆包,replaceWith 整段替换

凭空造一个节点

写页面时,很多节点是“后来才出生”的:新一条待办、新一张卡片。造节点的方法很轻巧—— 把尖括号写进 $() 里。注意与找元素的选择器区别:$('li') 是“找现成的 li”, $('<li>') 是“新造一个 li”。

第 2 步里全是熟面孔:text()、addClass()、attr() 都能链式连写。 appendTo 是 append 的反向写法——一个说“容器装元素”,一个说“元素进容器”,挑顺手的用。

放进去:append / prepend / after / before

安放方位一共四个,分清“子级”与“同级”就不乱: append() / prepend() 以父元素为主语,插进子级的末尾 / 开头; after() / before() 以元素自身为主语,插到同级的后面 / 前面。

例 1 · append / prepend / after / before 四个方位

我是本体:append / prepend 的徽章进框内,after / before 的徽章在框外。

每个徽章都是现场 $('<span>') 造出来的,再按四个方位安放。 框内框外的差别,就是“子级”与“同级”的差别。

拿掉:remove / empty / detach

有增就有删,三种删法各管一摊:

方法效果
$el.remove()删掉元素自身与全部子孙,连同绑定的事件与数据一起清除
$el.empty()只清空里面的内容,元素自身这个“壳”还在
$el.detach()同 remove,但保留事件与数据——日后还能原样插回来

日常口诀:“清空列表用 empty,连根拔起用 remove, 暂时离场还想回来用 detach。”

合起来练:列表的增与删

把“造、放、删”串成一个小工具:输入任务、点击添加,列表末尾多一项;点“删除”按钮,那一项离场。 删除按钮是后来才出生的,所以用上一式学过的事件委托——把事件绑在列表身上。

例 2 · 添加与删除列表项(事件委托)
  • 每日一页 HTML

拆解两个关键:$(this).closest('li') 从删除按钮向上找到它所在的那一项再 remove(); 事件绑在 #jq08-list 上、用选择器 '.jq08-del' 过滤—— 于是以后每一条新加的任务都自带删除功能,不用再绑一遍。

复制:clone() 与 clone(true)

clone() 把元素连结构一起复印一份;但它不复印事件—— 事件是登记在“原来那个元素”身上的。想让复制品也有灵魂,用 clone(true), 连事件与数据一起带走。

例 3 · clone() 与 clone(true) 的差别

复制后点击各卡片:只有原版和 clone(true) 复制品会“已阅”变红。

修行卡

点我:已阅。

心法:委托与 clone(true) 各管一路

例 2 的删除按钮用的是事件委托(事件在父级 ul 身上), 新元素天生可响应,无需 clone(true);例 3 的事件直接绑在卡片自己身上, 复制时就必须 clone(true) 才能带走。两条路都通,知道自己在哪条路上就行。

包与换:wrap / replaceWith 与反向插入

节点操作还有两路细活:一路是“包装”——给现成的内容加一层外衣; 一路是“替换”——把旧节点整段换掉。它们各有几个方法,配对很好记:

方法做什么
wrap(结构)给每一个选中元素各包一层
wrapAll(结构)把所有选中元素合起来包进同一层
wrapInner(结构)只把元素内部的内容包起来(外壳保留)
unwrap()拆掉选中元素的父级(跟 wrap 反着来)
replaceWith(新内容)把选中元素整段换成新内容
insertAfter(目标) / insertBefore(目标)与 after / before 效果相同,只是“主语宾语”互换
例 4 · 包一包、换一换
晨练 临帖 夜读
依次演示不同的包法;点「还原」随时回到最初。
练功 · 第八式
  1. 给例 2 加两个按钮——“清空 empty()”和“连根拔起 remove()”,点完再看列表还在不在。
  2. 把例 2 的 appendTo 换成 prependTo,让新任务插到列表顶部;并说明为什么放用户输入要用 text() 而不是 html()。
  3. 思考题:为什么 clone() 复制出来的卡片点不动,clone(true) 复制出来的就行?
参考答案 1

empty 之后列表壳子还在,还能继续添加;remove 之后 ul 整个消失, 再点“添加”就没地方放了。所以“清空列表”要用 empty()。

参考答案 2

prependTo 与 prepend 对应,插到开头。安全上:text() 把输入当纯文字; html() 会把输入当 HTML 解析——用户输入 <script>alert(1)</script> 之类的内容就会被执行,这叫 XSS 攻击。放用户输入,永远用 text()。

参考答案 3

事件是登记在“原来那个元素”身上的:clone() 只复印了 HTML 结构, 没复印 jQuery 记在元素上的事件与数据,所以复制品点不动;clone(true) 才连事件一起带走。 反观例 2 的删除按钮用的是事件委托(事件在父级身上),新元素天然可响应——委托场景下不需要 clone(true)。

本式小结

  • $('<li>') 凭空造节点,text / attr / addClass 妆点它;
  • append / prepend 插进子级末尾 / 开头,after / before 插到同级后 / 前,appendTo 等是反向写法;
  • remove() 连事件一起删,empty() 只清内容,detach() 删而留事件;
  • clone(true) 连事件一起复制,复制品也有灵魂;
  • wrap 各自包、wrapAll 合成包、wrapInner 包内容、unwrap 拆父级;replaceWith 换掉整个节点。
下一式,页面不刷新也能取数据——Ajax。