CSS 样式防御

CSS 样式防御

概念 / 起因

什么是样式防御?
顾名思义,就是保护你写的样式最后是你想展现的样子。一般样式被破坏多发生于第三方应用编程中,当编写的组件被应用于第三方网站中,组件的样式经常会被网站本身的样式影响而破坏了它本来想展现的样子。

为什么会研究这个东西呢?因为有一天,我引用的第三方代码的 CSS 被我网站上的 CSS 影响了。于是,我噼里啪啦赶紧把第三方的样式又给“强调“了一遍。搞完一想,不对啊,这特么应该是第三方自己没做好对自己 CSS 代码的防御啊,我瞎操什么心呢。不过这也引出了这次的话题,假如是你来写这个第三方应用,你怎么去做这个样式防御?

对比研究

为此,我特地先研究了几家比较知名的网站是怎么做的。
首先,我们先看一下畅言 http://changyan.kuaizhan.com/static/help/a-api.html,这是一个第三方评论组件,打开 console 仔细看了一下,发现其实它没怎么做样式防御,大部分样式写在 id 选择器里,防止了大部分的误伤。但还是很容易误伤,比如加个 span { color: red; } 评论就都变红色了。不过它在 logo 上倒是加了 !important 来防止隐藏,还有广告也加了一堆 !important,效果嘛,你懂得。

接着我们来看一下 Disqus https://blog.disqus.com/why-readers-pay-for-news 是怎么做的。没错,iframe,直接赶尽杀绝。不让任何人修改我的样式,你只能用我提供的样式。用 iframe 固然可以完美的做到样式防御,不过开发的复杂度一点也没有减少,动态的计算组件的高度来改变 iframe 的高度就够让人头疼了,毕竟里面评论一条套着一条,一条行数都是不定的。不过简单的固定长宽的倒是很适合用 iframe 来开发,兼容性也好。

还有 Twitter,

用了 Shadow DOM,也算是时代先锋了。
我们先来看看 Shadow DOM 是什么,https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/%E5%BD%B1%E5%AD%90_DOM

Shadow DOM 修复了 CSS 和 DOM。它在网络平台中引入作用域样式。 无需工具或命名约定,您即可使用原生 JavaScript 捆绑 CSS 和标记、隐藏实现详情以及编写独立的组件。

看描述,感觉非常适合用来编写第三方组件,内心一喜,但是别激动,查一下兼容性先。
Shadow DOM 兼容
果不其然,天妒英才啊,不过,这不妨碍我们了解一下它。
从文档中我们知道 Shadow DOM 必须附加在一个元素上,可以是HTML文件中的一个元素,也可以是脚本中创建的元素;可以是原生的元素,如<div>、<p>;也可以是自定义元素如 <my-element>。 如下例所示,使用 Element.attachShadow() 来附加影子DOM:

1
2
3
4
5
6
7
8
<my-element id="hostElement"></my-element>
<script>
// create shadow DOM on the <p> element above
var shadow = document.querySelector('#hostElement').attachShadow({mode: 'open'});
shadow.innerHTML = '<p>我是 Shadow DOM~</p>';
// 添加CSS,将文字变红
shadow.innerHTML += '<style>p { color: red; }</style>';
</script>

这里的 p 就不会受到外部 CSS 的影响了,由此可见,Shadow DOM 很好的隔绝了外部的世界,天然的实现了样式防御。

对比到这里,大家对样式防御也有了一定的了解了。那么除了上述的三种方案,还有什么方案吗?
我们来想想为什么要做样式防御,不就是怕第三方网站的样式污染了我们组件的样式吗,如果我们能把第三方网站的样式在我们组件这里重置掉不也可以吗。
网上有很多重置 CSS 的代码,虽然写那么多略显繁琐,但有用。那有什么一句代码就能重置的呢?有吗?当然是有的了,不然我也不会写出来了。就是 CSS all 属性了。下面的一句代码,就能把标签的样式恢复到浏览器默认状态了。

1
2
3
element {
all: initial;
}

不过很不幸,依然不兼容全系列 IE
CSS all 兼容
不过比 Shadow DOM 好多了,如果你的应用不需要兼容 IE 的话,可以一试。

总结

作为一个第三方组件,比如评论组件,光隔绝外部样式是不够的,准确的说,是不够智能,假如外部设置的字体,颜色跟我的组件差别很大,那我的组件不就显得格格不入了吗。所以我们隔绝了外部样式后还得获取外部的核心样式来让我们的组件显得更和谐。具体要获取哪些样式,就仁者见仁智者见智了,根据你的组件来定制,没有一成不变的规则。