💫 Chrome 浏览器的更新导致 jQuery 反复发版,只因 :has 这个伪类

阿轩的BUG
文章191
评论56
标签101
站点日志
作者总数:1位
置顶文章:0篇
标签总数:101条
文章总数:196篇
评论总数:56条
微语总数:149条
运行天数:2314天
最近更新:2024-09-30
文章标签
最新评论
Sherlock
2025-08-21
不是应该打包成apk文件吗,怎么突然就运行编译了,下一步呢
老金er
2025-05-25
人生设计非常必要,在信息化发达的今天,周围大部分人依然只能过着人生工程模式生活,长时间生活洗礼后,尽管“人生工程”已经不再适合很多人,他们也无法做出任何改变,人生设计给我们提供了新的思路,值得尝试
赏帮赚
2025-05-25
职场太多的尔虞我诈
三笑
2024-08-16
emlog6.0.1可以升级支持吗?
pony
2024-07-08
这篇文章提供了一种新的人生规划方法——"人生设计",它借鉴了产品设计的理念,鼓励人们以创造性和探索性的方式思考和规划自己的生活。以下是对这篇文章的几个评价维度: 创新性:将产品设计的理念应用于人生规划是一个新颖的视角,为人们提供了一种跳出传统生活轨迹,探索个性化生活路径的方法。 实用性:文章不仅提出了理念,还详细介绍了具体的实施步骤,包括自我评估、制定计划、原型设计和实践选择等,具有很强的操作性。 启发性:通过强调好奇心、不断尝试、重新定义问题等心态,文章鼓励人们打破常规,勇于探索未知,这对于激发个人潜能和创造力具有积极作用。 适应性:"人生设计"理念适用于不同人生阶段和不同背景的人,无论是希望从头开始的人,还是已经取得一定成就但希望探索新方向的人,都可以从中获得启发。 情感共鸣:文章对许多人对传统生活轨迹的不满和对改变的渴望进行了深刻的洞察,能够引起读者的共鸣。 可持续性:人生设计强调的是一个持续迭代和试错的过程,这与现实生活的发展规律相吻合,有助于人们在不断变化的环境中做出适应性调整。 教育意义:作为一种人生规划方法,"人生设计"可以作为教育的一部分,帮助年轻人更早地学会自我探索和规划,为未来的生活和职业发展打下基础。 局限性:尽管"人生设计"提供了一种新的思考框架,但它可能需要个人具备一定的自我认知和反思能力,对于一些缺乏自我探索经验的人来说,可能需要额外的指导和支持。 总体来说,这篇文章提供了一种有价值的人生规划方法,对于那些渴望改变、寻求个性化生活的人来说,是一种有益的参考和指导。
首页值得一看 🔍️

前端到底是“死”没死我是不知道,但是 jQuery 似乎又“回光返照”了一下,窜稀式地连更了几个小版本。

我仔细看了看,并没有新功能出现,不过还挺有意思的,也让我学习到了新的东西。简单来说,这几个版本的更新主要与 Chrome 引入新选择器产生的缺陷有关。那么谷歌浏览器是如何导致 jQuery 产生 Bug 的,又是为何在修复之后又引发了新 Bug 的,这还得从 :has 伪类说起。

:has() 伪类

CSS 在很早前就存在子代和后代选择器,而父元素的选择器出于性能的考虑却迟迟没有浏览器能支持,虽然规范早已存在,但是真正支持它却是不久前才发生的事。

这个伪类通过把可容错相对选择器列表作为参数,提供了一种针对引用元素选择父元素或者先前的兄弟元素的方法,例如:

h1:has(+ p) { color: red; }

这段 css 表示的是:选择一个 h1 标签,当它有一个相邻节点是 p 标签时,这个 h1 标签文字会显示为红色,蛮好理解的对吧。

该伪类在 2022 年由 Safari 浏览器首先开始支持,随后 Chrome 105 也启用了这个原生的 :has 伪类,那么这个新选择器的支持为什么会给 jQuery 带来 Bug 呢?

都是 Chrome 浏览器的锅

其实 jQuery 长期以来一直支持 :has 伪类,甚至扩展了 :contains 这样的 api 出现在选择器中,它是以错误抛出(try-catch)的形式来判断应该使用浏览器的 querySelectorAll 还是 Sizzle(jQuery 开源的一套 css 解析器)来匹配样式的。

例如 :has(:contains("Item")) 这种形式的选择器,在以前会视为错误,所以当触发异常抛出时 jQuery 就会使用自己的解析工具从而保证选择器能正常工作。

但是现在 Chrome 更新了浏览器的 :has 原生支持,且作为参数的是可容错相对选择器列表,于是上述这种形式的写法直接被忽略,并不会报错,按照 jQuery 原本的处理方式,这里不抛出异常的话就会直接使用原生选择器,而原生选择器接收到错误的参数又直接“罢工”,所以导致了 jQuery 所有可追溯到最早版本的 :has 选择器都被破坏了。

这里 jQuery 团队用了一个梗作为段落标题:“打破互联网的并非拉尔夫”(” It wasn’t Ralph that broke the internet.” ——拉尔夫是迪士尼电影《无敌破坏王2:大闹互联网》中的主角)我的理解是在说凡事都有两面性,新事物也许是把双刃剑的意思吧,属于是委婉表达 :has 是个好东西但 Chrome 处理得有问题,虽然 jQuery 出了 Bug 但宝宝真的冤啊!

不过 Chrome 团队也很快实施了一种解决方法来修复以前的 jQuery 版本,而 Safari 因为对 :has() 的实现的处理方式略有不同,没有遇到同样的问题。(Chrome:好了别说了,要不你再重启一下电脑试试?应该就没问题了😋)

“允许的”并不代表就是“正确的”

上文提到了 :has 包含的参数是可容错的,遵循所谓“宽容解析”( forgiving parsing )原则,怎么理解呢?我们先来看看什么是“无效的选择器列表”,如下有一段 CSS 规则集:

h1 {
  color: red;
}
h2:invalid{
  color: red;
}
h3 {
  color: red;
}

很明显,上面 :invalid 是错误的一个伪类,所以 h2 的那段样式将是无效的,但通常我们还会这么写:

h1, h2:invalid, h3 {
  color: red;
}

请注意,这两段规则集只在完全正确时是等效的,而下面这段规则集里只要存在一个不正确,整个规则都将不会被解析,也就是说 h1h2 不会有样式被应用了。

聪明的你应该想到了,可容错选择器就是在这个背景下诞生的,以 :is 为例,将第二段规则集改为如下写法,就与第一段完全等效了:

:is(h1, h2:invalid, h3) {
  color: red;
}

:is:where:has 这些伪类都是相似的,因为参数列表可容错,所以它允许你传入可能有错误或不支持的参数,并且不会因为错误而“中断”选择器。

修复 Bug 往往会引发另一个 Bug

起初 jQuery 团队想到的修补方案,是通过判断包含了 :has 的选择器则强制使用 jQuery 的选择器引擎来解析,但这并不灵活,在社区的讨论声中大家普遍认为 jQuery 不应该依赖 try-catch,于是在 3.6.2 中 jQuery 开始改用 native 的策略 CSS.supports 来确定选择器是直接传递给 querySelectorAll 还是通过 jQuery 的选择器引擎。打住,这又是什么我没见过的黑魔法,吓得我赶紧翻了翻 MDN 文档。

CSS.supports() 有两种不同的传值形式。

第一种用来检验浏览器对于一对“属性 - 属性值”的支持,例如:

CSS.supports("display", "flex");
CSS.supports('--foo', 'red');

另一种传入一个包含检测条件的字符串:DOMString,例如:

CSS.supports(\'(--foo: red)\');
CSS.supports("( transform-origin: 5% 5% )");

这种方法是支持选择器作为字符串来检测的,例如:

CSS.supports("selector(span)") // true

这样就可以检测特定的伪类规则是否在浏览器中支持啦:

通过这个 supports("selector(SELECTOR)") 来确定一个选择器直接传递给 querySelectorAll 是否有效,返回 false 则退回到到自己的选择器引擎(Sizzle),这似乎是个理想的方案,不过 jQuery 团队明显对这个静态方法并不熟悉,所以在实现上犯了一点小错误,因为 SELECTOR 只支持 <complex-selector> 而不能是 <complex-selector-list>,例如:

CSS.supports("selector(div)"); // true
CSS.supports("selector(div, span)"); // false

这意味着所有复杂的选择器列表都通过 Sizzle 解析而不是 querySelectorAll,所以
jQuery 又发了一个新版本 3.6.3 来修复这一问题。

但是,事情还没完,很快 jQuery 又发现这种实现方式仍然是有缺陷的😅

有某些选择器在过去工作得很好,但 CSS.supports 却好像并不认为它们可以正常工作,例如原生的 querySelectorAll.("[attr=val") 是可以正常使用的,但无论是 CSS.supports( "selector(:is([attr=val))" ) 还是 CSS.supports( "selector([attr=val)" ) 都是返回 false.

我在当前浏览器 Chrome 110 中测试结果和官方不太一致,看来谷歌又偷偷改了点东西,根据 jQuery 官方的说法只有 Firefox 是相对稳定的。

在收到多条 issues 之后,jQuery 考虑恢复到以前的方式,同时,在 jQuery 与规范编写者和供应商的讨论中,一致认为需要防止类似 :has 的问题在未来再次发生,虽说收到一些“保证”,但考虑浏览器还需要一段时间去逐渐更新,所以在 3 月又发布了 3.6.4 这个版本并建议用户升级。

上一篇
😲 SQL语句中 left join 后用 on 还是 where,区别大了!
下一篇
🚀 Vite4.3 来袭,性能提升 10x!冷启动时间比turbopack还快!