偶遇 HTML 中的奇妙字符

本文最后更新于:2020年12月30日 凌晨

虽然名字听起来就像是一本正经的胡说八道,但吾辈确实遇到了一个奇怪的问题,于此分享给大家。

事情的起始如下

下班回家 => 想要看动画 => 去动漫花园下载 BT 种子 => 动漫花园一片空白 => Why?

默认 uBlock 屏蔽页面

于是吾辈偷偷的的打开了控制台看了一下,发现是页面中的内容元素不见了。经过深思熟虑(好吧其实也就是稍微想了一下)首先把 uBlock 禁用,毕竟这个最容易被网站检测出来并且对抗嘛!果不其然,页面恢复了正常,但。。。同时广告也出现在了页面上。

默认 uBlock 不屏蔽页面

这可不行,重新启用了 uBlock 看了一下分析,很显然,内容不存在大概率是被 uBlock 的元素过滤功能隐藏掉了,查看被隐藏的内容元素,发现 id 为 1280_adv,但同时又包含了广告与主体内容,所以只要关掉 uBlock 的元素过滤就可以避免正常内容被误杀了。

之所以不在该网站整个禁用掉 uBlock 的原因在于 uBlock 并不只有元素过滤,它还阻止了一些广告资源的加载,仅在动漫花园就包括但不限于 baidu.com, bebi.com, histats.com。显而易见,禁用它们还能提高加载速度。

uBlock 屏蔽的脚本

既然无法使用 uBlock 的元素屏蔽了,那么吾辈便需要使用一个新的方式去阻止广告了,幸运的是吾辈安装了 Stylus 和 Tampermonkey 插件。

Stylus 能够使用被称为 user.css 的技术,能够在本地修改任意网站的样式 – 即自定义 UI 显示。
而 Tampermonkey 则更强大,支持 user.js – 可以在本地打开任意网站时载入自定义的 JavaScript 脚本,不再局限于修改 UI,几乎与插件无异(事实上它也确实被认为是更轻量的插件)。

原以为就几句 css 的事情,找到了广告的 id,于是吾辈写下了下面这些 css

1
2
3
4
5
6
7
/*屏蔽动漫花园的广告*/
.ad,
#1280_adv,
#1280_ad > a,
#bebiv3_ad {
display: none;
}

但结果却是。。。只生效了一半!

屏蔽一半

可以看到,上面两个广告确实被隐藏了,但下面一个却并没有,而且吾辈在控制台直接使用 document.querySelector('#1280_adv') 也获取 dom 会抛出错误 SyntaxError: Document.querySelector: '#1280_adv' is not a valid selector。吾辈是直接复制的 id,理论上来说不会有错才是。

仔细想想,吾辈或许是漏掉了什么。。。于是,吾辈使用 Copy => Copy Selector 功能,有趣的东西出现了,复制出来的内容竟然是 '\31 280_adv',wtf?

嗯,或许吾辈需要冷静一下,尝试使用 document.querySelector("#\\31 280_adv") 获取一下

注意:这里 JS 里面去查询 DOM Selector 的字符串又进行了转义。

Console 获取

OK,确实能够正常拿到。由于这些奇怪的字符在 css 中存在语法错误,那么接下来便用 user.js 去屏蔽掉它们吧!

基本实现如下

1
2
3
4
5
;[
document.querySelector('#\\31 280_adv'),
document.querySelector('.ad'),
document.querySelector('#\\31 280_ad > a'),
].forEach((ad) => ad.remove())

甚至吾辈都发到了 GitHubGreasyFork 上了。

然后,有个(万能的)网友就提出,可以转换思路,既然 #\\31 280_adv 在 css 中存在语法错误,那么使用属性选择器过滤 id 将值包裹在 '' 之中不就好了么?此话真是九言劝醒迷途仕,一语惊醒梦中人,吾辈瞬间 GET 到了这个点。

于是吾辈编写出了下面这段 user.css 样式

1
2
3
4
5
6
7
/*屏蔽动漫花园的广告*/
.ad,
div[id='\31 280_adv'],
div[id='\31 280_ad'] > a,
div[id='bebiv3_ad'] {
display: none;
}

使用后效果如下

屏蔽后干净的网页

现在,初始目的达成了,吾辈开始有点好奇它是怎么实现这个功能,于是下载了它的源码,id 那里并未发现什么奇怪的东西,但吾辈却也无法复现一个 demo!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<div id="test">2</div>
<script>
window.addEventListener('load', () => {
document.querySelector('#test').setAttribute('id', '\\31 280_adv')
})
</script>
</body>
</html>

demo 效果

demo 效果

如果有人知道原因的话,请务必不吝赐教!

参考:ASCII Wiki


后续,万能的网友 NiaMori 又来说明啦,实际上是 id 以数字开头的原因,具体问题参考:
是 id 以数字开头的原因,简单的 <div id="1">test<div> 就能复现这个效果。
document.getElementById('1') 能够选中,但 document.querySelector('#1') 不能,因为 HTML5 允许 id 以数字开头而 CSS 不允许
0x31 是 ‘1’ 的 Unicode 编码值,Copy selector 的时候 Chrome 做了一个智能的 escape
参考:


偶遇 HTML 中的奇妙字符
https://blog.rxliuli.com/p/37d3c50950c24c5e940412df559c401d/
作者
rxliuli
发布于
2020年12月30日
许可协议