如何确定HTML元素是否具有伪元素?

9
有没有一种方法可以确定给定的HTML元素是否应用了伪元素( ::before / ::after ),而不必调用?
window.getComputedStyle(element, ":before/:after");

已编辑:答案是不行

getComputedStyle存在性能问题。我需要验证页面上的每个元素是否具有伪元素,这就是为什么性能非常关键的原因。 我尝试从document.styleSheets访问cssRules来查找以::before或::after结尾的选择器,将其删除并查找与其余选择器匹配的元素,但此解决方案有自己的问题:

  • 无法访问来自不同域的styleSheets
  • 可能有数千个cssRules要检查(再次考虑性能)

我还尝试比较具有和不具有伪元素的元素的大小和偏移量,但没有任何作用。即使没有准确的方法,我也很乐意找到一种方式,可以至少减少30%的要检查的元素数量。


当我确定元素具有伪元素时,最终可以调用getComputedStyle来调查伪元素样式并更改它。这部分完美地工作。


已编辑:我无法控制源页面。页面样式可能会动态从不同的域上传。将我的代码视为内容脚本或库,例如必须更改所有伪元素的前景色,以基于该伪元素的其他CSS属性计算某些颜色。

因此,主要问题是更改基于其他CSS属性进行的,您无法为所有伪元素设置相同的样式,而不实际检索伪元素的计算样式并计算更改。

例如,您拥有youtube.com页面,需要查找所有伪元素并

  • 如果此伪元素具有背景图像,则缩放元素
  • 如果此伪元素仅包含文本,则将其颜色更改为红色
  • 等等...

你能提供更多关于你正在做什么以及为什么要这样做的细节吗?也许你想要实现的目标可以通过不同的方式来完成。 - Alexander Nied
不确定这是否真的会改善事情,但您可以排除像 imginputtextareaselect 等不允许伪元素的替换元素。 - Gabriele Petrioli
这很有趣 - 我不知道有些元素不能有伪类 - 我会检查一下。谢谢。 - Pavel Agarkov
@GabyakaG.Petrioli,实际上,规范中关于“替换元素”的行为尚未定义的注释已经不存在了,而Firefox 50似乎也将伪元素应用于它...因此,OP唯一可以进行的过滤是对元素与非元素节点进行过滤。 - Kaiido
@Kaiido 酷,谢谢你的信息。 - Gabriele Petrioli
显示剩余5条评论
2个回答

3

您现有解决方案存在哪些问题?

遍历元素的这种方法:

var allElements = document.getElementsByTagName("*");
for (var i = allElements.length; i--;) {
  //var usedStyle = window.getComputedStyle(allElements[i], ":before/:after");    
  ...
}

涉及每1毫秒处理数千个元素。

getComputedStyle的调用不应该太慢,因为它获取的数据已经在调用时(呈现文档上)进行了计算。

所以所有这些都应该足够快。是的,原则上这是一个O(N)的任务,但只运行一次,对吧?


“而且getComputedStyle的调用不应该太慢,因为它获取的数据在调用时已经计算好了。” - 不,事情并不那么简单。调用getComputedStyle会导致回流,请参见https://dev59.com/n3RB5IYBdhLWcg3wyqKo#1278213。 - CBroe
是的,它非常慢 - 例如对于YouTube页面,它需要近5秒钟的时间! - Pavel Agarkov
最终证明并不是很慢 - 我只是同时运行了另一个进程,拖慢了一切。因此我将坚持使用getComputedStyle。 - Pavel Agarkov

2

我认为getComputedStyle()是唯一可靠的方法来知道一个元素是否有伪元素附加。

但由于没有办法从DOM方法中编辑它,这意味着您必须在文档中添加一个新规则。

因此,如果您像在最后一次编辑中所述那样将相同的规则应用于所有伪元素,则可以使用通配符*选择器来解决:

*:before,*:after{ /*your rules*/ }

var style = document.createElement('style');
style.innerHTML = `*:before,*:after{
  color: green !important;
}`;
setTimeout(function() {
  document.head.appendChild(style);
}, 1500);
div:before {
  content: 'hello';
  color: blue;
}
div:after {
  content: 'world';
  color: red;
}
<div></div>


是的,我现在使用类似的方法。唯一的区别是我为所有者元素设置了自定义属性: - Pavel Agarkov
[before-style="1231243523341"]:before { color: red; } - Pavel Agarkov
@pasza 请记住,已经定义的 :before:after 规则可能比您的规则具有更高的特异性,因此您的样式可能不会被应用。 - Gabriele Petrioli
我在我的问题中忘记提到(现在已经提到),我需要根据每个伪元素的其他CSS属性进行不同的更改...因此,我仍然需要获取实际计算样式。因此,主要问题是如何确定元素是否具有伪元素。 - Pavel Agarkov
是的,这就是为什么我实际上使用这样的东西:[before-style="1231243523341"]:not(important):before { color: red !important; }但即使这样也不能保证结果... - Pavel Agarkov
@pasza,这正是我担心的。那么,你所做的就是唯一的方法。我曾发布过一条已删除的评论,说您可以过滤掉伪元素不适用的替换元素,但实际上,规范中关于此的说明已经消失了,即使我在最新版本中找不到任何东西,Firefox已经将伪元素应用于替换元素。因此,您唯一的过滤选项是仅针对元素进行定位,这是c-smile的非常正确的答案(IMM您应该接受它)。 - Kaiido

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接