DOM 遍历:沿树行走的清扫路线


文档摘要

3.1 DOM 遍历:沿树行走的清扫路线 接手一个别人写了一半的页面,最常见的工作状态是:手里只有一个事件发生处的元素,目标却在树的另一头。选择器是"从全文档找人",遍历则是"从手头这个人出发,向下、向旁、向上走"。本节把三条行走路线的方法族一次走全——它是本章其余三节的取货通道,属性、样式、结构修改要操作谁,全靠这里先走到。 本节摘要:遍历方法按方向分三路:向下有 (直接子代)与 (全部后代中按选择器再筛),向旁有 、 、 及其 All 系列,向上有 、 、 。每次行走都返回新的 jQuery 集合, 方法可以在链上退回上一步的集合。 向下走:children 与 find 的分野 规则与 2.2 节的空格、大于号完全同构: 对应大于号(只要儿子), 对应空格(子孙都要)。

3.1 DOM 遍历:沿树行走的清扫路线

接手一个别人写了一半的页面,最常见的工作状态是:手里只有一个事件发生处的元素,目标却在树的另一头。选择器是"从全文档找人",遍历则是"从手头这个人出发,向下、向旁、向上走"。本节把三条行走路线的方法族一次走全——它是本章其余三节的取货通道,属性、样式、结构修改要操作谁,全靠这里先走到。

本节摘要:遍历方法按方向分三路:向下有 children(直接子代)与 find(全部后代中按选择器再筛),向旁有 siblingsnextprev 及其 All 系列,向上有 parentparentsclosest。每次行走都返回新的 jQuery 集合,end 方法可以在链上退回上一步的集合。

向下走:children 与 find 的分野

$('#ward').children(); // 全部直接子元素:h2 与 ul $('#ward').children('ul'); // 直接子元素里再留 ul $('#ward').find('li'); // 后代里所有 li,嵌套多深都算 $('#ward').find('.urgent'); // 后代里带 urgent 类的元素

规则与 2.2 节的空格、大于号完全同构:children 对应大于号(只要儿子),find 对应空格(子孙都要)。一个实用差别是 find 必须带参数,而 children 可以不带——"看看它有几个孩子"这种探查动作,children() 后看 length 即可。

向旁走:同辈兄弟的探望路线

$('.urgent').siblings(); // 同父的全部兄弟,不含自己 $('.urgent').next(); // 紧邻的下一个 $('.urgent').prev(); // 紧邻的上一个 $('.urgent').nextAll(); // 身后所有兄弟 $('.urgent').prevUntil('.done') // 上一个直到 .done 之前(不含两端)

这与层级选择器的加号、波浪号是同一件事的方法形态。什么时候用选择器、什么时候用方法?经验法则是:关系路径能用一句选择器说清就用选择器,需要"先拿到一个动态元素再找邻居"(比如事件回调里的 this)就必须用方法——第 4 章的事件处理里你会不断见到后者。

向上走:parent、parents 与 closest

向上走的三个方法各有分工,混用是高频错误源:

$('.urgent').parent(); // 直接父元素:只有 ul 一个 $('.urgent').parents(); // 全部祖先:ul、main、body、html 依次入列 $('.urgent').closest('.panel'); // 从自己出发向上找,第一个带 panel 类的祖先

closest 的两个细节值得划线。其一,查找范围包含元素自己——$(this).closest('li') 在回调里几乎是条件反射式的写法:不管点中的是条目里的哪个子元素,都能退回承载事件的那个条目。其二,结果最多一个,parents 带选择器则可能拿到多个。第 4 章的事件委托会把 closest 用成标配。

图:DOM 树三条行走路线

图:DOM 树三条行走路线

行走的记账规则:集合在链上怎么变

遍历方法的返回值是新集合,原集合被压进栈里。end 就是把栈顶弹出来的那一步:

$('#queue') .find('.urgent') // 集合变为:急症条目 .addClass('flag') .end() // 退回:整个 queue 的原集合 .find('.stable') // 再从原集合出发找稳定患者 .addClass('watch');

不用 end 的等价写法是把两段拆开存变量,可读性见仁见智;但读别人的代码必须认识 end,否则链到一半"集合悄悄换人"会看得一头雾水。同理,filternot 也是"返回新集合"的成员,链式语义一致。

原生对照:三条路线的本来走法

jQuery 原生 备注
$el.children('ul') el.children 后自行 filter 原生 children 不收参数
$el.find('.urgent') el.querySelectorAll('.urgent') 语义一致
$el.next() el.nextElementSibling 原生没有对应 next 的容错封装
$el.closest('.panel') el.closest('.panel') 原生同名同义,现代浏览器可直接用
$el.siblings() 无直接对应 常用 [...el.parentNode.children] 减去自己

closest 已经是 DOM 标准,这是 jQuery 反哺原生语言最出名的例子之一——当年它在类库里发明,如今成了每个浏览器自带的方法。

集合的并轨:add 与 addBack

两批结果要合成一批时用 add;想在换集合之后"把旧集合也带上"用 addBack

$('#queue .urgent').add('#beds .urgent'); // 两个区域的急症并成一批处理 $('#queue').find('.urgent').addBack(); // 急症条目 + queue 本身,一起加描边

addBack 的典型岗位是"容器与命中项一起高亮":find 换集合后把出发站也拉回来,一条链写完,不用回头再查一次容器。

病例:向上找错了人

背景:标本页的就诊卡片嵌在面板里,需求是"点击卡片任意位置时给整张面板加描边"。一次会诊顺手写了 $(this).parents('.panel'),功能正常;两周后样式升级给卡片外又包了一层带 panel 类的内衬,页面一刷新,描边加在了内衬上。

排查与修法:parents 返回全部匹配祖先,操作类方法作用在每一层上——层级一变,多出来的那层也被波及。这里要的是"最近的、恰好一层",正解是 $(this).closest('.panel'):不管中间将来再插多少层装饰容器,命中的永远是离自己最近的那个 panel。解读:三个向上方法的选择其实是一道"要几层"的单选题——parent 恰好一层且不筛类型、closest 最近的一层且按选择器筛、parents 来者不拒全栈收下。选错不报错,只在结构变化那天发作。变式:若真需要"从卡片往上到面板之间的每一层都改",才轮到 parentsUntil('.panel'),两端不包含,区间语义正好。

要点复盘

  • 向下 children 只要儿子、find 子孙全要;向旁 nextprev 走一步、siblings 全体同辈;向上 parent 一步、parents 全族、closest 就近含己。
  • 动态上下文里(事件回调、循环体内)优先用遍历方法,选择器留给静态的、全文档级的查找。
  • closest 包含自身且至多命中一个,是委托与冒泡场景的标准动作。
  • 遍历返回新集合,end 退回上一站;链式调用前后集合是否"换人"要随时有数。
  • 原生 closestchildrennextElementSibling 覆盖了大半高频需求,迁移前先对照表格。

人已经走到病灶跟前。下一节翻开元素的档案:特性和属性这两本账,为什么记的常常不是同一个数。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U