伪原创 方法(标题里写的是伪元素,不过这篇文章怎么处理)
优采云 发布时间: 2021-10-02 07:06伪原创 方法(标题里写的是伪元素,不过这篇文章怎么处理)
虽然伪元素写在标题中,但是这个文章主要指的是::before和::after,其他伪元素(::first-letter, ::first-line, ::selection等)由于没有内容属性,所以这篇文章就一一介绍了。其实方法是一样的。
伪元素的关键在于一个伪元素。虽然可以被浏览器渲染引擎识别并正确渲染,但是伪元素本身并不是DOM元素,所以不能直接被js操作——所以任何直接选择DOM元素的基于JS的CSS变化方法对伪元素不起作用-元素。(JQ好像无所不能,这个问题很直接。因为JQ选择器是基于DOM元素的)对于JS和JQ选择器,可以参考这两个文档:Selectors API Level 1、jQuery Selectors
获取伪元素的属性值
虽然JS中没有可以直接操作伪元素的选择器,但是还是有办法获取它的CSS属性的。
window.getComputedStyle
使用window.getComputedStyle方法选择伪元素,然后使用getPropertyValue方法获取对应属性的值。
根据 MDN 文档,
window.getComputedStyle(element[, pseudoElt]);
该方法收录两个参数,一个是元素本身,另一个是元素的伪元素。
js 语法示例(完整的 DEMO 在线链接):
var div=document.querySelector('div');
var fontSize=window.getComputedStyle(div,'::before').getPropertyValue('font-size');//获取before伪元素的字号大小
关于这个方法的详细介绍请参考这个文章:
熟悉获取元素CSS值的getComputedStyle方法
更改伪元素的属性值
window.getComputedStyle 方法虽然可以获取到伪元素的属性值,但是根据方法名,它只能获取CSS样式,不能改变css属性。那么如果想用js动态改变伪元素的属性值,应该怎么做呢?如何处理?
思路如下:
js改变data-*属性的值来改变伪元素的内容值
创建多个类,通过切换类达到改变风格的目的
使用 CSSStyleSheet 的 insertRule 方法添加样式
使用内部css样式的高优先级覆盖外部css
以上实现思路的推荐度依次递减
使用 DOM 的 data-* 属性改变 content 的值
data-*是HTML5新增的DOM元素属性,其作用大致可以理解为一个标记。具体用法可以参考这篇MDN文章文章。除了常规赋值,伪元素的content属性值还有一个特殊的attr()方法来获取。
HTML:
CSS:
.test::before{
content: attr(data-text);
}
结果:
TEXT
另外,content其实可以写成多个attr,而attr()可以是DOM元素的任意属性(比如class等,甚至非W3C标准的属性也支持,但是不推荐这样做),所以采集一些模板文本非常方便。像下面这样写是完全没问题的。注意使用空格连接,不要使用“+”号。
考试:
.test::before {
content: '我的类是' attr(class) '想要变成' attr(data-color);
}
虽然W3C给了attr()无限的可能,包括颜色、宽度等属性,希望以后能改一下这个方法,不过目前只有content支持这个方法,其余的还处于draft状态,暂时还没有浏览器支持。之所以把这个方法放在首位,是因为和其他的实现方法相比,这个方法实在是太简单了,太优雅了。
但是如果你真的想改变伪元素中的颜色和其他元素呢?
更改类以更改伪元素的样式
把这个方法放在推荐的第二位,估计会被很多人骂:“操,这么简单又不强制的方法,居然能把它放在第二位!太低级了。” 但是看完后两种方法,你可能对这个看法有了一些改变。
这种方法的优点是使用方便,没有兼容性问题。缺点是类比较多,实际用处不大,很像jQuery类选择器中毒患者的做法;另外不适合多状态场景(比如伪元素文字大小的实时变化等)。
实现太简单了,没有贴代码。
之前的班级转大法可能会让人觉得不舒服,这里有一个(伪)的做法:
使用 CSSStyleSheet 的 insertRule 方法添加样式
这部分内容更多涉及W3C标准。另外,它比较冷门,关注的人也不多。个人目前无法掌握标准。所以这部分内容就不深入分析了,理解上可能有问题。
CSSStyleSheet 是浏览器存储页面中所有 CSS 样式表(不包括内联样式)的对象方法。每个链接和样式标签代表一个 CSSStyleSheet 对象。您可以使用 document.styleSheets 方法来获取它们。(需要注意的是,styleSheets方法返回的结果虽然收录了link标签引入的外部样式,但非IE浏览器无法获取其cssRules属性,只能获取嵌入的style标签中的元素)
document.styleSheets[0].insertRule('.test::before{color:green}',0)//chrome,firefox等非IE浏览器使用
document.styleSheets[0].addRule('.test::before{color:green}',0)//IE系列浏览器使用
/* 虽然部分浏览器也可以通过id来指定,'document.styleSheets.id.insertRule()'这种写法在chrome和IE下都行得通,但是firefox会返回'undefined',所以建议还是使用index值来获取stylesheet */
.insertRule的语法是stylesheet.insertRule(rule, index),另一个参数是index,表示cssRules样式表在对应styleSheets中的位置。值越大,样式优先级越高,但该值不能超过当前样式表规则(cssRules)的长度(CSS中定义的第一个样式总是会被后面的定义覆盖)。当该值小于 cssRules 的长度时,添加的样式规则将插入到索引值定义的位置。其余规则将被推迟。
addrule 和 insertRule 方法本质上是一样的,但是后者不被 IE 浏览器识别,所以前者作为浏览器兼容的方法存在。(下面为了节省篇幅,用insertRule方法来引用这两个方法。)
上面的代码在一行中看起来很简单,但并不是每次都有效。原因如下:
虽然document.styleSheets按照style和link的顺序返回对应的StyleSheetList,但是如果第一个是link而不是style,如前所述,此时无法获取对应的cssRules,那么document.styleSheets[0].cssRules为 null, insertRule 方法不起作用。(这种情况只针对非IE浏览器,IE浏览器正常,但早期的定义往往意味着被后一种风格覆盖,所以意义不大)
同上,如果页面中没有嵌入样式的样式标签,insertRule 方法也不起作用。
如果索引值不够大,很可能早于css文件开头定义的位置,导致被覆盖。因此,妥协是在添加的样式中添加 !important ,尽管我个人不喜欢它。
这说明了这种方法的局限性,但这种方法的优雅之处在于它避免了直接编写内联样式,而是通过 css api 进行更改。比起下面的方法,稍微好一点。
但是这个方法好像有点局限?
在HEAD中添加style标签,强制覆盖初始属性
这种方法是利用内部css样式的高优先级来覆盖外部css。优点是简单易懂,易于实现。缺点是食物太丑太粗糙。
var style=document.createElement('style');
style.innerHTML=".test::before{color:green}";//添加样式内容的话也可以用上面提到过的`insertRule`,相对例子里的硬编码会更优雅点。
document.head.appendChild(style);
看到这里,有些人可能已经反应过来了。实际上,添加样式标签的方法可以作为insertRule实现方法的大前提——因为并不是所有的页面一开始都内置了样式样式。这种方法虽然不是很好,但有时确实是有必要的——比如“拖动滑块改变伪元素中文字大小”的需求。
实践功能需求
拖动滑块更改伪元素中文本的大小
并且在伪元素中随时显示当前字体大小
可以通过按钮改变伪元素中文本的颜色
这个要求可以满足本文前面提到的所有四种更改伪元素样式的方法。具体实现参考DEMO,不再具体分析:
参考
getComputedStyle()
Window.getComputedStyle()
熟悉获取元素CSS值的getComputedStyle方法
属性符号
W3C CSS3-属性参考:'attr()'
MDN 属性()
CSS规则
CSSRule 对象
MDN 插入规则
使用 JavaScript 向样式表添加规则
使用 JavaScript 修改伪元素样式
将 css-rules 添加到现有样式表