任意网站注入自定义 CSS 样式

需求来源

有些网站的广告元素很容易找,想直接把它 display:none 掉,或者把某些带背景图像的元素隐藏或更换背景图片。 然后找到了一个 Chrome 插件 Stylus 也能用但感觉没有必要单独装一个插件,还很复杂的样子。 想着油猴脚本应该有很多类似的,结果搜了一下都是针对特定网站的(比如百度/B站/知乎美化),于是就自己写了这个油猴插件。 效果图

使用方式

需要先安装用户脚本管理器 Tampermonkey (即俗称的油猴插件)或者 Violentmonkey (中文名 暴力猴 ),然后点击 这里 安装脚本,点击 这里 预览脚本(见 Gist 地址 )。 对油猴脚本开发感兴趣的同学,可以参考之前的文章:如何编写一个油猴脚本

实现原理

一个 URL 正则表达式对应一条 CSS 规则,将所有规则保存起来(油猴提供了两个 API GM_getValue / GM_setValue 供存取设置内容)。 然后在每个页面加载后,遍历所有规则,找到所有对本页适用的规则,直接注入到页面中。 本着一个脚本做一件事的原则,这个脚本只提供注入 CSS 功能,而不提供注入 JS 的功能。

局限性

在浏览 GNU 的 ftp 站点 时发现规则没有生效。然后认识了一个 HTTP 响应头 Content-Security-Policy 。 该站点的此响应头是这样设置的:
1
Content-Security-Policy: <span>default</span>-src <span>'self'</span>; img-src data: <span>'self'</span> https:<span>//</span>static.fsf.org https:<span>//g</span>nu.org; object-src <span>'none'</span>; frame-ancestors <span>'none'</span>; child-src <span>'self'</span> https:<span>//</span>static.gnu.org https:<span>//</span>static1p.gnu.org https:<span>//</span>static1p.fsf.org;script-src <span>'unsafe-eval'</span> <span>'self'</span>
报错信息如下:
1
Refused to apply inline style because it violates the following Content Security Policy directive: <span>"default-src 'self'"</span>. Either the <span>'unsafe-inline'</span> keyword, a hash (<span>'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='</span>), <span>or</span> a nonce (<span>'nonce-...'</span>) <span>is</span> required to enable inline execution. Note also that <span>'style-src'</span> was <span>not</span> explicitly set, so <span>'default-src'</span> <span>is</span> used <span>as</span> a fallback.
这个 header 对应的值是多个分号分隔的部分,指定了页面支持哪些来源的资源。 第一个 default-src 指定默认的资源来源是 self 即当前域名。 根据报错信息提示,浏览器拒绝应用注入的样式,因为内容安全策略设置的 default-src 指定了只接受同域名的样式,而没有接受行内样式 unsafe-inline ,也没有指定可执行的样式的 hash, nonce 值。 因为没有指定 style-src 所以使用的是 default-src 规则。 更多内容安全策略的相关知识,大家可以参考 MDN 文章 内容安全策略( CSP )Content-Security-Policy ,以及阮一峰的博文 Content Security Policy 入门教程

分享一些规则

找到了广告元素后,可以直接使用 display: none 将其隐藏,另外插件还注入了一个水印层, class 是 .inject-watermark ,也可以用来添加自定义水印~
1
2
3
4
5
6
7
8
9
<span>body</span> > <span>div</span><span>:empty</span> {
  <span>/* body 的空的子元素去除背景 */</span>
  <span>background</span>: transparent <span>!important</span>;
}

<span>.inject-watermark</span> {
  <span>/* 插件注入的元素,覆盖整个页面。可以添加自定义水印 */</span>
  <span>background-image</span>: <span>url</span>(<span>"data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Ctext x='50%25' y='50%25' font-size='14' fill='%23a2a9b6' fill-opacity='0.5' font-family='system-ui, sans-serif' text-anchor='middle' dominant-baseline='middle' transform='rotate(-45 50 50)'%3Eyouthlin.com%3C/text%3E%3C/svg%3E"</span>)
}
svg 可以作为背景水印,如何将文本写在 svg 上,可以参考菜鸟教程 SVG <text> 。 从教程的 尝试一下 按钮可以进入 svg 在线编辑器。 另外再推荐张鑫旭的几篇相关文章: 如何让文字作为CSS背景图片显示? ; 如果想要将文字倾斜,还可以看下 svg 中如何旋转 理解SVG transform坐标变换 ; 最后为了把 svg 放在背景上,通常可以使用 base64 编码 svg 后放在 background-image 的 url 中, 不过,还有另一种可读性更好的方式 学习了,CSS中内联SVG图片有比Base64更好的形式

Reader Echoes

1 comment

  • #1544Youth.霖
    <pre> div:not([class]) > div:not([class]) > div:not([class]) > div:not([class]) > div:only-child[class^=w] { display: none !important; } </pre> 可以隐藏这样的结构: <pre> &lt;div> &lt;div> &lt;div> &lt;div> &lt;div class="wxxx"> </pre>

表情

评论提交后需经审核才会显示。