ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

jQuery 9类核心选择器详解:从定位到过滤的实战指南

jQuery 9类核心选择器详解:从定位到过滤的实战指南 如果你接手过任何一个 2013 年之前起步的企业级项目大概率会对这句话有共鸣jQuery 早就不是前端的主流选择了但生产环境里的老代码比新框架多得多。改需求的时候你绕不开$()那一长串表达式。而选择器又是 jQuery 的地基九成以上的链式操作都从$()里的那个字符串开始。我这次梳理的就是 jQuery 的 9 类核心选择器基本选择器、层级选择器、基本过滤选择器、内容过滤选择器、可见性过滤选择器、属性选择器、子元素过滤选择器、表单选择器、表单对象属性过滤选择器。这篇内容适合三类人一类是维护老项目、被各种选择器组合绕晕的前端一类是刚接触 jQuery、想用最短时间建立体系的新人还有一类是写惯了 Vue/React、偶尔回老项目改需求却忘了选择器语法的老手。我不打算做那种把 API 列表抄一遍的教程而是把每一类的底层逻辑、最容易踩的坑、以及真实场景里的组合打法都讲清楚。读完之后你能做到一件事看到需求马上知道该用哪一类选择器怎么写最稳。1. 为什么是9类先看清这套体系的全貌1.1 分类背后的两条主线定位与过滤很多初学者拿到选择器文档第一反应是背。背完之后真写页面还是懵因为文档是按类别组织的而需求是按场景组织的。我建议换个角度看jQuery 的选择器实际上在解决两个层面的问题。第一层是定位。你要从文档里把元素找出来就得告诉它在哪一块、什么名字、和别的元素是什么关系。基本选择器按 id、class、标签名直接找层级选择器按父子兄弟关系扩展找子元素过滤选择器按相对父级的位置找——这些都是定位逻辑。第二层是筛选。元素已经找到了一堆但你只想要其中特定的几个。基本过滤选择器按索引筛内容过滤按文本筛可见性过滤按显示状态筛属性过滤按标签属性筛。表单选择器和表单对象属性过滤则是为 input、button、select 这类控件定制的快捷筛选。这两条主线对应到 jQuery 的源码实现上也很有意思能走原生querySelectorAll的选择器会走原生走不了的比如:contains、:eq、:has就退回 Sizzle 引擎手动遍历。理解这条边界你就明白为什么有些选择器快、有些选择器慢这个后面专门讲。另外多说一句这 9 类里有大半和 CSS 选择器语法重叠但 jQuery 又扩展了一批 CSS 标准里没有的伪类比如:eq、:contains、:has、:odd/:even。网上很多入门教程把 CSS 元素选择器当作第 1 关到了 jQuery 你会发现写法眼熟但语义上有微妙差异别想当然照搬。1.2 一张速查表把9类全部装下在展开之前先放一张我常年贴在工位上的速查表。它不覆盖每个选择器的全部细节但足够你在写代码的时候一眼定位该用哪类。类别代表写法解决什么问题基本选择器#id、.class、element、*按 id / class / 标签名直接抓元素层级选择器div p、divp、divp、div~p按文档里的父子、兄弟关系定位基本过滤选择器:first、:eq(n)、:odd、:not(...)在已选集合上按索引或状态二次筛选内容过滤选择器:contains()、:empty、:has()按元素包含的文本或子元素筛选可见性过滤选择器:visible、:hidden按显示状态筛选属性选择器[name^val]、[name$val]等按标签上的属性及其取值筛选子元素过滤选择器:first-child、:nth-child()按元素在其父级中的位置筛选表单选择器:input、:text、:checkbox等快速定位表单控件表单对象属性过滤选择器:enabled、:checked、:selected按表单控件的启用/选中状态筛选先整体记住这张表再往下看每一类的细节和坑。看的时候别只盯着语法多想想它对应哪种需求这样遇到实际问题才接得上。2. 基础与层级选择器每天写得最多边界也最容易模糊2.1 基础选择器id 是性能王者class 是主力星号是坑基础选择器是绝大多数人学会的第一批内容这里我只讲三个容易忽略的认知。第一个是性能差异的来源。$(#id)底层映射的是原生getElementById这是浏览器里最快的查找方式.class和element在旧版 jQuery 里映射的是getElementsByClassName和getElementsByTagName在 3.x 版本里则统一走querySelectorAll。所以如果你在一个大页面上频繁查找元素优先用 id 或者把范围先框到某个 id 容器里再往里找。第二个是通配符*。$(*)在文档里匹配所有元素代价极高尤其是和层级选择器组合成$(div *)这种形式时几乎等于把 div 下面所有层级的全部标签都遍历一遍。除非是写类似统计页面节点数的脚本否则不要在业务代码里用。第三个是并集写法。$(div, .item, #footer)用逗号把多个表达式拼在一起返回的是每个表达式的元素并集。这个写法很实用但要注意别把一个元素重复包含比如$(.item, #wrap .item)如果.item本身就在#wrap里同名元素只会返回一次而不是两次jQuery 会自动去重。这一点和 CSS 的选择器语义一致但很多人没想到 jQuery 也会去重实测中发现返回长度比预想短就慌了。基础选择器最需要养成的习惯是作用域意识。写$(.item)之前先问问自己页面里是不是到处都有.item如果答案是肯定的就改成$(#container .item)既能缩小范围也避免和后加的模块撞车。老项目里经常有全站公共样式类名直接裸用.class选出来的往往是一大堆不相干的元素查都查不完。2.2 层级选择器空格、大于号、加号、波浪号四种关系别混用层级选择器一共有四种符号语义差异极大很多 bug 就出在这几个符号身上。div p空格选中 div 内部所有后代 p包括嵌套在 div 里的其他 div 中的 p。div p大于号只选中 div 的直接子元素p孙辈一律不管。div p加号选中紧跟在 div后面相邻的 p且要求两者有同一个父级。div ~ p波浪号选中 div 之后的所有兄弟 p不需要紧挨着但同样是同一个父级。我通常用生活化的类比记空格是几代人都算大于号是只算亲生儿子加号是紧挨着的邻居波浪号是身后整条街的邻居。最容易踩的坑是和~的混淆。只匹配紧邻的那一个元素~匹配后面所有符合条件的兄弟。比如h2标题/h2 p第一段/p p第二段/p span中间夹个 span/span p第三段/p$(h2 p)只会选到第一段$(h2 ~ p)会选到第一段第二段第三段这三个因为它是按之后的所有兄弟来算的中间夹个 span 不影响。如果你只想要紧挨着的那个写成多写了一个~结果就是多选一堆改都不好改。另一个常见问题是空格和大于号搞混。$(div p)在业务代码里经常被拿来选div 里的 p但如果 div 里面又套了一个 div内层 div 的 p 也会被选出来很多时候这不是你要的效果。要的是直接子级就用语义更精确排查也更省心。还有性能点这种子代选择器在 jQuery 老版本里会被转成$(parent).children(filter)的组合因为当时 Sizzle 对的处理方式特殊。3.x 之后大部分情况能走原生 CSS快很多。如果你还在维护 1.x/2.x 的老项目遇到页面卡顿可以查查是不是层级选择器嵌套太深。3. 过滤选择器从一堆元素里精确抽出你要的那几个3.1 基本过滤选择器eq、gt、lt、odd/even 的索引陷阱基本过滤选择器处理的是位置概念。:first和:last分别取集合中的第一个和最后一个这俩没有争议。真正容易出问题的是索引类。:eq(n)的索引从 0 开始所以:eq(0)是第一个元素:eq(1)是第二个。:odd和:even也是按 0 起始索引算的odd选中索引为 1、3、5 的元素even选中索引为 0、2、4 的元素。看起来简单写表格斑马纹的时候就会出经典 bug// 想让第 1、3、5 行有背景色 $(tbody tr:odd).addClass(stripe); // 错选中的是视觉上的第 2、4、6 行 $(tbody tr:even).addClass(stripe); // 对因为第一行的索引是 0属于 even我在代码评审里见过不止一个人把:odd和奇数行划等号结果表格前几行颜色全反了。原因就是索引基数是 0 而不是 1。记牢一句话:odd选的是索引为奇数的元素不是页面里第奇数个的元素。提示斑马纹如果要从第一行开始加色用:even或者:nth-child(odd)别用:odd。:gt(n)和:lt(n)分别表示索引大于 n和索引小于 n。这类带负数索引的写法在不同 jQuery 版本里行为有差异不可控。现在要选除了第一个以外的所有元素我会直接写$(li).slice(1)语义比:gt(0)更直白性能还更好。还有一个实用组合是:not()过滤。$(li:not(.active))在修改集合时非常常用比如把一个列表里所有非激活项置灰。:not内部可以再接选择器表达式但别嵌套太深否则可读性会变得很差。3.2 内容过滤与可见性过滤contains 的大范围、empty 的隐藏条件内容过滤选择器一共四个:contains()、:empty、:has()、:parent。:contains(关键词)用来按文本内容选元素。它有两个坑。第一它是大小写敏感的第二它会命中所有包含该文本的祖先元素。比如div classbox p这里有一段话/p /div$(div:contains(一段话))会选中这个div.box因为它内部的 p 文本包含一段话文本节点是从祖先开始逐层包含的。如果你只想选到 p 本身得再配合其他条件比如$(p:contains(一段话))而不是指望:contains只匹配最近的文本容器。:empty看起来简单实际上有一层隐藏细节元素里没有任何内容才匹配包括文本节点和元素子节点都算内容。所以div/div匹配div /div不匹配因为里面有一个空格文本节点。很多动态生成的空容器因为带了换行或空格:empty怎么都选不中排查半天才发现是模板里多了一个缩进空格。:has()是包含某个子选择器的元素按子元素来判定。$(div:has(.alert))选中所有内部有.alert的 div。这个选择器性能开销不小因为 jQuery 要对每个候选元素做一次子匹配。能用find()加filter()替代的场景我会优先用后者可读性和性能都更好。可见性过滤两个选择器:visible和:hidden。最大的坑在 jQuery 对隐藏的定义它认为一个元素只要占据文档空间就是可见的哪怕你用visibility:hidden或opacity:0把它藏得看不见:visible还是会命中它。因为visibility:hidden是占布局的所以 jQuery 把它算作可见opacity:0也占布局同样算可见。只有display:none、typehidden的 input、宽高为 0、或者祖先display:none的情况下才被算作:hidden。所以判断某个动画元素是否真的显示别用$(#el).is(:visible)去判断透明度动画的结束状态它永远给你 true。我后来学乖了凡是涉及opacity或visibility的状态判断都改用类名或 data 属性自己管状态别依赖 jQuery 的可见性过滤。3.3 子元素过滤选择器first-child 与 eq(0) 根本不是一回事这一节值得单独写因为:first-child和:first、:eq(0)之间的区别是新手最容易搞混的。:first是整个匹配集合里的第一个:first-child是每个父元素下的第一个子元素。看例子ul liA/li liB/li /ul ul liC/li liD/li /ul$(li:first)只选中 A$(li:first-child)会选中 A 和 C因为它们在各自的 ul 里都是第一个孩子。同理$(li:last-child)选中 B 和 D$(li:eq(0))只选中 A。如果你想要的是每个分组里的第一个必须用:first-child如果你只想要整个页面的第一个first和eq(0)都可以但:first写法更简洁。:nth-child(n)更是重量级它的索引从1开始和:eq的 0 起始完全相反。:first-child等于:nth-child(1)。它还支持odd、even和公式写法比如:nth-child(3n1)表示从第一个开始每三个取一个1、4、7、10……这个公式是 CSS 语义不熟悉的记两个常用变体:nth-child(odd)取父级下的奇数位子元素1、3、5:nth-child(3n)从第三个开始每三个取一个3、6、9。注意:nth-child的奇偶和前面:odd/:even的奇偶基数不同。表格场景下经常有人选错我直接放个对比表需求推荐写法实际选中的行第 1、3、5 行加背景tr:nth-child(odd)父级中的第 1、3、5 个子元素第 1、3、5 行加背景tr:even匹配集合索引 0、2、4即视觉第 1、3、5 行第 2、4、6 行加背景tr:nth-child(even)父级中的第 2、4、6 个子元素第 2、4、6 行加背景tr:odd匹配集合索引 1、3、5即视觉第 2、4、6 行这个小表格我建议截图存下来。斑马纹选错行是 jQuery 项目里最经典的入门 bug没有之一。4. 属性选择器动态页面上最靠谱的定位手段4.1 六种基础形态外加两种扩展属性选择器按元素有没有某个属性、属性值长什么样来筛选。基础形态有六种[attr]元素具有该属性不管值是什么。[attrvalue]属性值完全等于 value。[attr^value]属性值以 value 开头。[attr$value]属性值以 value 结尾。[attr*value]属性值包含 value 子串。[attr!value]属性值不等于 value。注意这不是CSS 标准选择器是 jQuery 的扩展——它的匹配规则是要么没有这个属性要么属性值不等于 value。另外还有两个 CSS 标准内、但平时用得少的[attr~value]匹配以空格分隔的词正好包含 value 的属性值比如classa b能被[class~a]命中[attr|value]匹配属性值以 value 开头且后面紧跟连字符常用于语言标记如[lang|zh]匹配zh-CN。先说[attr!value]的坑。很多人把它理解成属性值不等于 value 的所有元素但 jQuery 的实现会把没有这个属性的元素也算进来。举个我实际遇到的例子界面上有一批按钮部分带>$(a[href^http]).addClass(external-link);场景二按文件类型做下载事件埋点$(document).on(click, a[href$.pdf], a[href$.zip], function() { trackDownload($(this).attr(href)); });场景三处理动态数据列表。接口返回的数据渲染成一堆div每个都带>$(.task-item[data-statuscompleted]).addClass(done);场景四回到热词里那个左上角元素的需求。有时候产品经理会说把左上角那个元素挪一下但页面上没有给左上角这个容器专门加 id。不要试图去找一个坐标选择器jQuery 根本没有这种东西。正确思路是先用父级容器把范围框住再用子元素或属性组合定位$(#header .logo)、$(#header [data-roletitle])之类。把位置需求翻译成结构需求是选择器组合的核心能力。另外使用属性选择器时要注意特殊字符的转义。如果属性值里包含引号或方括号常规写法会断掉推荐用变量拼接加转义或者在filter()里用函数比较。比如var target user[name张三][active]; $(div).filter(function() { return $(this).is(target); });is()接收一个选择器字符串做匹配判断比硬拼接正则舒服得多。5. 表单选择器处理交互开发绕不开的一整套5.1 表单选择器类型快捷方式与两个经典误解表单选择器是 jQuery 特有的一套类型快捷方式最常用的有:input、:text、:password、:radio、:checkbox、:submit、:image、:reset、:button、:file。:input匹配所有input、textarea、select、button元素注意它和input标签选择器并不等价后者只匹配input。这个区别在做表单清空之类操作时很实用$(#myForm :input).val()比逐个标签写高效得多。两个经典误解我几乎每次评审都能遇到。第一个:text不等于textarea。:text只是input[typetext]的简写textarea是另一个元素要用$(textarea)单独选。有人写$(form :text)想一次性清空所有文本框和文本域结果 textarea 没被选中表单里还有残留文本。第二个:button不等于所有按钮。:button匹配button元素和input typebutton但不匹配input typesubmit和input typereset。要选提交按钮得用:submit。我接手过一个老模块搜索所有按钮用的是:button结果那个表单的提交键始终没被样式规则生效排查了很久才意识到typesubmit的 input 根本没被选进去。表单选择器还有一个通病如果不限定作用域:text、:checkbox这种写法在整个文档里扫代价不小。老项目里的最佳实践永远是先框表单容器$(#loginForm :checkbox:checked)而不是$(:checkbox:checked)。这样既快又避免多个表单相互干扰。5.2 表单对象属性过滤enabled、disabled、checked、selected表单对象属性过滤选择器按控件当前状态筛选:enabled、:disabled、:checked、:selected。这里最大的坑是:checked和:selected的适用范围完全不同。:checked只对input里的checkbox和radio有效:selected只对option有效。混用的结果是一个select下拉框里选中的 option:checked是选不中的必须用:selected。反过来勾选的 checkbox用:selected也选不中。如果你写$(:checked)想同时覆盖两种选中状态在页面同时有下拉框和复选框时结果会包含两类元素处理时很容易搞混。实际开发里读选择框的选中值我基本不写$(select option:selected).val()而是直接用$(#citySelect).val()一步到位。jQuery 的 val() 对 select 原生支持没必要绕一圈。:selected真正用得多的地方是当作遍历入口比如把某 select 当前选中的选项文本拼出来var cityText $(#citySelect option:selected).text();$(input:checkbox:checked)这类组合也很典型。注意这里有两个伪类叠在一起:checkbox先筛出类型:checked再筛出选中状态顺序可以反过来写成$(input:checked:checkbox)只要两个条件同时满足就行。还有个状态选择器:focusjQuery 1.6 才正式加入。判断当前焦点在哪个输入框上用$(:focus)很方便但注意它同样有性能问题涉及焦点变化的高频回调里别反复执行整个文档的:focus查找。通常我会在 focus 事件里把当前元素缓存到变量而不是每次去查。6. 组合实操从表格高亮到悬浮完整内容单项选择器讲再多不串起来用等于白学。这一节我用三个真实场景把前面的 9 类选择器揉在一起演示。6.1 表格斑马纹与行高亮过滤选择器加事件委托先是最常见的后台列表页。表格要有斑马纹还要支持点击行高亮。斑马纹用前面说的:even或:nth-child都行只要基数对了第一行开始加色用:even或nth-child(odd)。// 斑马纹注意作用域限定在 tbody避免表头被误伤 $(#reportTable tbody tr:even).addClass(row-stripe); // 行点击高亮用事件委托动态新增的行也能生效 $(#reportTable tbody).on(click, tr, function() { var $row $(this); $row.addClass(row-selected) .siblings(.row-selected) .removeClass(row-selected); });这里用到了三样东西:even做斑马纹、tr标签选择器限定点击范围、.siblings()方法配合类选择器清理其他高亮行。注意我特意把siblings(.row-selected)写在addClass后面利用链式操作把选中当前行和取消兄弟行串成一次查找性能比再$(#reportTable tbody .row-selected)全局找一遍好得多。如果你预期行里还有子元素直接绑在tr上会有点击穿透问题可以加一层判断$(e.target).closest(tr)但那属于另一个事件细节这里先不展开。6.2 单元格超长悬浮展示完整内容后台表格的高频需求后台表格经常遇到这种需求单元格内容太长界面上只显示省略号鼠标悬浮时要能看全部。这也是我检索热词时看到最多人问的场景之一直接贴一套可复制的方案。先给超长单元格加样式.cell-overflow { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }然后在前端渲染的时候给超过长度的单元格加cell-overflow类并把完整内容存到>$(document).on(mouseenter, .cell-overflow, function() { var $cell $(this); $cell.attr(title, $cell.attr(data-full-text) || $cell.text()); });如果你要控制样式和位置就得自己做气泡。通过offset()拿到单元格位置动态生成一个 div 跟在旁边悬浮结束再移除$(document).on(mouseenter, .cell-overflow, function() { var $cell $(this); var fullText $cell.attr(data-full-text) || $cell.text(); var $bubble $(div classcell-tooltip/div) .text(fullText) .appendTo(body); var offset $cell.offset(); $bubble.css({ left: offset.left, top: offset.top $cell.outerHeight() }); $cell.data(bubble, $bubble); }).on(mouseleave, .cell-overflow, function() { $(this).attr(title, ); var $bubble $(this).data(bubble); if ($bubble) { $bubble.remove(); $(this).removeData(bubble); } });这里的关键是mouseenter/mouseleave必须通过事件委托绑在document上或者绑在表格容器上。因为很多后台表格是动态渲染的直接$(.cell-overflow).on(...)只在第一次渲染时生效后续翻页新增的行全都不响应。用on(mouseenter, .cell-overflow, fn)的委托写法只要事件冒泡到容器就能命中所有满足选择器的元素。如果表格在一个滚动容器里记得在滚动时同步更新气泡位置或者直接在滚动时把气泡移除不然气泡会漂在旧坐标上。6.3 表单状态批量处理属性选择器加对象属性过滤第三个场景是表单。假设一个权限配置页面列了一堆复选框和下拉框要求勾选了全选后所有权限复选框变成选中并启用取消后全部取消并禁用。$(#selectAll).on(change, function() { var isChecked $(this).is(:checked); // 用属性选择器定位权限区内的所有复选框 $(#permissionPanel input:checkbox) .prop(checked, isChecked) .prop(disabled, !isChecked); });再比如提交前校验找出第一个未填写的必填文本框聚焦并报错。var $firstEmpty $(#applyForm input:text[required]).filter(function() { return $.trim($(this).val()) ; }).first(); if ($firstEmpty.length) { $firstEmpty.trigger(focus); alert(请先填写标红的字段); }这个例子里属性选择器[required]筛掉了非必填项.filter(function(){...})做了值判断.first()取得第一个目标。.filter()接收函数时函数里this是每个被遍历的原始 DOM 元素记得先$(this)再调用.val()直接this.val()会报错这也是新手高频报错点。7. 排查与性能选择器写对了页面体验就好一半7.1 选择器选不中的四个高频原因我维护老项目时每隔一段时间就会收到某个元素选不中的求助。绝大多数情况逃不出下面四类原因。第一DOM 还没就绪就执行了选择器。脚本放在head里且没用$(function(){...})包裹$(#app)在 DOM 构建完成前执行自然选不到。解决方式是统一把初始化代码放进 document ready 回调。第二特殊字符没转义。id 或 class 里带点号、方括号、冒号很常见比如iduser.name。直接用$(#user.name)会被解析成id 为 user 且带 class name的组合永远选不中。正确写法是$(#user\\.name)点号前面加反斜杠。同理属性值里有引号时也要小心。这个坑我第一次踩的时候排查了半小时。第三元素是动态生成的。列表页翻页、弹窗内容、接口渲染的节点都是执行$()之后才出现的。如果在初始化的$(.item).on(click, fn)上绑定事件新出现的元素不会有任何反应。要么用事件委托$(document).on(click, .item, fn)要么在渲染完成后重新查找绑定。第四作用域嵌套导致集合为空但看起来应该能选到。比如弹窗里有个.modal-body .btn-primary你写$(.modal .btn-primary)选不中因为.btn-primary不在.modal里而在.modal-body里。层级选择器的每一层都要严格对照真实 DOM不能凭视觉印象写。排查的时候我最常用的一招是直接在浏览器控制台跑一句$(你的表达式).length。先确认长度是不是 0如果是 0再逐层缩短表达式比如先跑$(.modal)再跑$(.modal .btn)定位到底哪一层断掉。这一步通常一分钟就能锁定问题。提示排查选择器第一步永远是$(表达式).length长度不对再谈别的。7.2 性能对比哪些选择器最该省着用选择器的性能差距在几十个节点的页面上感知不明显到几百行表格、复杂弹窗、级联下拉这种场景就会暴露。核心规则是能用原生 CSS 引擎处理的选择器jQuery 会交给querySelectorAll只有 jQuery 扩展的伪类才被迫走 Sizzle 手动遍历。类型是否走原生 CSS 引擎典型写法基础 / 层级是#id、.class、div p属性是[typecheckbox]标准结构伪类是:first-child、:nth-child(2n)jQuery 扩展伪类否走 Sizzle:eq()、:odd、:contains()、:has()、:visible走手动遍历的选择器必须先取出候选元素再逐个过滤数量一大就很慢。实际优化建议在循环里不要反复写带伪类的全文档查询先把结果缓存var $rows $table.find(tr);再对$rows做:eq(i)。能用.slice()、.filter()、.first()、.last()等链式方法就少用:eq、:gt。能先用 id 缩范围再往里find()别一上来就是$(#a #b .c)这种多层级表达式。事实上#a #b这种写法本身就不推荐换成$(#b)或者$(#a).find(#b)更符合就近查找的原则。绑定事件时高频事件scroll、mousemove里别放选择器查询即使要查也把查到的结果缓存到外部变量。还有个大坑是选择器字符串拼接变量时不加转义。比如$(# id)如果 id 里有特殊字符等于给自己埋雷。稳妥做法是$(document.getElementById(id))既绕开转义问题性能也更好。7.3 一个调试习惯把选择器当函数而不是魔法字符串写多了你会发现选择器最大的价值不在于少写几行getElementById而在于它是一套声明式的、可组合的查询语言。我个人的习惯是碰到复杂选择器先在控制台验证长度和内容再放到业务代码里碰到选择器组合看不懂的拆成两步——先查范围再过滤条件。比如$(#userPanel .item:not(.disabled):has(.badge))这种链式叠加拆开写反而更容易排查var $base $(#userPanel .item); var $active $base.not(.disabled).has(.badge);.has()方法是:has选择器的方法版语义一样可读性更好性能差别不大。这种选择器表达式和方法链混用的做法是我在维护老代码时最推荐的调试方式既保留了选择器的声明式优点又让每一步的结果都能打断点观察。8. 写在最后选择器不是语法是解决问题的思路前面七节把 9 类核心选择器都过了一遍该带的坑也基本带到了。最后分享一点我在真实项目里的体会。我最早接触 jQuery 时也走过背 API的弯路。后来发现真正拉开效率差距的不是谁记得多而是谁先学会把需求翻译成选择器组合。产品说表格隔行变色我脑子里浮现的不是:odd那个单词而是我要在父级下按位置取行注意索引基数产品说悬浮看完整内容我想到的是事件委托加 data 属性的组合拳。选择器是语法更是解决问题的思路这两者缺一不可。另一个体会是老项目里选择器出 bug很少是单个写错更多是上下文没管好——作用域没框、动态节点没考虑、索引基数搞反。所以后来我给自己定了三条铁律任何选择器都先圈定 id 容器再往下找任何事件绑定都优先用委托任何涉及顺序和状态的选择器先在控制台用.length验证再放行。这三条救了我不下十次。如果你正准备接手一个 jQuery 老项目不用把全部 API 背完把 9 类记住再把三个实战案例抄一遍基本就能应付日常需求了。剩下的细节遇到的时候翻文档比记笔记靠谱。选择器这套体系放到今天依然值得学因为 Vue、React 里很多 DOM 操作思路追根溯源还是那套结构定位 状态过滤的老手艺。把 9 类选择器吃透去折腾新框架只会更顺。
返回列表