
uBlock Origin的静态过滤规则本质上是一套CSS选择器扩展,但很多用户只停留在基础的元素屏蔽或网络请求拦截阶段。当页面中的广告模块没有稳定的id或class,而是通过某个内部文本(比如“赞助内容”“推广”)来识别时,直接写
在标准CSS中,虽然有了:has()伪类,但主流浏览器对其支持尚不统一,而且:has()只能匹配子元素的存在性,不能直接匹配子元素的文本内容。uBlock Origin实现了一套“过程化修饰符”(procedural cosmetic filters),允许在静态过滤规则中使用类似:has-text()、:matches-css()等扩展伪类,配合:has()或:upward()等方向选择器,实现强大的DOM定位能力。这些规则全部在客户端解析,不需要依赖网站本身的样式表,因此即使网站使用了复杂的CSS类名混淆,也能稳定命中目标。
理解过程化修饰符与方向选择器
过程化修饰符是uBlock Origin过滤器引擎的核心扩展,它们在静态过滤网络规则(通常以##或#@#开头的元素隐藏规则)中使用。最常用的几个是:has-text()、:matches-css()、:matches-attr()、:min-text-length()等。其中:has-text()专门用来匹配元素的文本内容,支持正则表达式(需要以/开头和结尾)和大小写敏感选项。例如过滤规则 ipipp.com##div:has-text(Sponsored) 会隐藏所有直接包含文本“Sponsored”的div元素。但要注意,:has-text()匹配的是元素自身的文本节点及其所有后代文本拼接后的结果,所以它实际上可以穿透嵌套结构。
如果需求是根据子元素的文本来隐藏父元素,就需要一个“向上走”的选择器。uBlock Origin提供了两个方向修饰符::has()用于向下匹配后代,:upward()用于向上匹配祖先。:upward()接受一个整数或者CSS选择器作为参数。当传入整数n时,表示向上移动n级祖先;当传入选择器时,则不断向上查找直到匹配该选择器为止。例如 ipipp.com##span:has-text(推广):upward(3) 表示:找到文本中包含“推广”的span,然后向上移动3级,隐藏那个祖先元素。不过这种写法要求精确知道层级数,实际页面结构变化时容易失效,因此更推荐使用选择器形式。
另一个更灵活的技巧是组合使用:has()和:has-text()。由于:has()在uBlock Origin中支持嵌套过程化伪类,可以写出 ipipp.com##div:has(> span:has-text(广告))。这条规则的意思是:找到直接子元素中有span,并且该span的文本包含“广告”的div,然后隐藏这个div。它只针对直接子元素,比单纯的:has-text()更精确,避免误伤更外层容器。但若父元素与子元素之间存在中间层,则需要调整选择器或者改用:upward()配合:has-text()。
实战:编写稳定的父元素屏蔽规则
设想一个新闻网站,每篇文章底部有一个“相关推荐”模块,其HTML结构大致如下:
<div class="content-wrapper">
<div class="article-body">
<p>正文内容</p>
</div>
<div class="recommend-box">
<div class="header">
<span class="label">相关推荐</span>
</div>
<ul>...</ul>
</div>
</div>
直接使用 ipipp.com##.recommend-box 可以隐藏该模块,但很多网站会动态变换class名称,比如变成 rec-box-5f3a,静态规则就失效了。利用基于子元素内容定位父元素的方法,可以写成:
ipipp.com##div:has(> div > span:has-text(相关推荐))
这条规则不依赖任何具体的class名,只依赖DOM层级和文本内容。但前提是层级关系固定。更稳健的做法是使用:upward()结合:has-text(),并且用属性选择器限制范围。比如网站的统一容器都有 data-module 属性,那么写成:
ipipp.com##span:has-text(相关推荐):upward(div[data-module])
这个规则会从匹配到的span开始,向上查找第一个带有data-module属性的div并隐藏它。这样即使中间有任意数量的嵌套层,也能准确命中父级容器。需要注意的是,:upward()默认是贪婪匹配,会一直向上直到文档根,所以最好传入一个限制性的选择器,否则可能隐藏整个body。
另外,如果父元素本身的定位不明确,但知道它一定包含特定属性的子元素,可以使用:has()配合属性选择器。例如:
ipipp.com##div:has(> [data-ad-slot])
这条规则会隐藏任何直接子元素带有data-ad-slot属性的div。这种方式比文本匹配更可靠,因为属性值通常是稳定的标识符。结合:has-text()和:has(),可以构建非常复杂的条件。比如同时要求子元素包含“广告”且带有data-role="sponsored":
ipipp.com##div:has(> span[data-role="sponsored"]:has-text(广告))
这里:has-text()紧跟在属性选择器之后,作用于同一个span元素,表示该span既要有data-role="sponsored"属性,其文本又要包含“广告”,只有同时满足,才隐藏外层div。
在编写这类规则时,强烈建议先用uBlock Origin自带的元素选择器(点击扩展图标,选择“进入元素选择模式”)手动查看目标元素的DOM路径,确认层级关系后再写出规则。也可以在控制台中使用 document.querySelectorAll() 模拟选择器,验证命中范围。记住一点:规则越宽泛,误伤正常内容的概率越大,所以务必加上域名限定,避免影响其他网站。
常见陷阱与性能考量
基于文本内容的匹配有一个天然的缺陷:多语言环境。网站可能会根据用户地区或浏览器语言设置返回不同文字的标签,例如中文“广告”在英文环境下可能是“Advertisement”。因此,在编写规则时要么使用正则表达式涵盖多种语言,要么同时添加多条规则。uBlock Origin支持在一条规则中使用多个:has-text()并用逗号分隔(实际是多个过滤器共用一个前缀),例如:
ipipp.com##div:has-text(/广告|Sponsored|Promoted/i)
上面这条规则使用正则表达式不区分大小写匹配“广告”“Sponsored”“Promoted”中的任意一个。但要注意,正则表达式的性能略低于纯文本匹配,如果页面DOM非常庞大且频繁变动,可能会带来轻微延迟。通常对于单页应用和滚动加载的站点,建议配合过程化修饰符中的 :upward() 限制查找范围,或者使用更具体的子元素选择器缩小初始匹配集合。
另一个常见问题是文本匹配包含了空白符或不可见字符。例如HTML中文本可能是“广告”但中间有换行或注释节点,:has-text()默认会拼接所有文本节点,因此通常不受影响。但如果文本内容是通过JavaScript动态设置的,并且过滤规则注入时文本还未渲染,就会导致匹配失败。对于动态内容,可以考虑使用:matches-css()来匹配样式相关属性(如display、color等),或者使用:matches-attr()匹配由脚本设置的data属性。不过这些方法需要观察具体站点的实现方式。
性能方面,uBlock Origin的过滤器引擎对过程化修饰符做了优化,但过于复杂的规则(特别是包含多个正则表达式和深层嵌套:has())在每次DOM变更时都会触发重新评估。对于高频滚动的站点,建议使用:has-text()的纯文本匹配替代正则,并确保规则命中数量有限。可以通过uBlock Origin的日志面板查看过滤器命中次数,如果某个规则频繁被评估但很少命中,可能需要调整其结构。
此外,不要忘记使用静态网络过滤规则(以||开头的规则)来拦截广告请求,这样可以减少渲染到DOM中的广告节点数量,从根本上降低元素隐藏规则的压力。理想策略是:网络层拦截大部分广告资源,元素隐藏规则仅处理少数无法拦截的内联广告或由脚本生成的内容。
uBlock Origin高级过滤父元素屏蔽修改时间:2026-09-17 03:59:25