如何循环页面上的所有元素,包括伪元素
How to loop over all elements on a page including pseudo elements?
如何在包括psuedo元素在内的所有元素上循环?我知道我可以使用getComputedStyle(element,pseudoEl)
来获取它的内容,但我一直找不到一种方法来获取页面上的所有伪元素,这样我就可以使用前面提到的函数来获取它们的内容/样式。这似乎是一个简单的问题,但一直无法找到任何解决方案。
你走在了正确的轨道上。使用getElementsByTagName("*")
或querySelectorAll("*")
,在所有DOM元素上循环是相当容易的。然后我们必须看看这些元素中的每一个,它们是否有一个伪元素。正如@zzzzBov提到的那样。
虽然您没有明确提到,但我认为:before
和:after
伪元素是您最感兴趣的元素。因此,我们利用了必须使用content
属性才能实际使用伪元素的事实:我们只是简单地检查它是否已设置。希望这个小脚本能帮助你:
var allElements = document.getElementsByTagName("*");
for (var i=0, max=allElements.length; i < max; i++) {
var before = window.getComputedStyle(allElements[i], ':before');
var after = window.getComputedStyle(allElements[i], ':after');
if(before.content){
// found :before
console.log(before.content);
}
if(after.content){
// found :after
console.log(after.content);
}
}
经过一些性能测试,我的建议是:
- 在大多数情况下,请使用Max K的解决方案。在大多数情况下,它的性能都足够好,它是可靠的,并且在15 LOC以下(我的大约是70)
- 如果你真的需要挤出每一毫秒的时间,并且你知道(因为你已经测试过了)它更快,那么就使用下面的解决方案
(通常)更快的解决方案
您已经知道如何使用document.querySelectorAll('*')
获取文档中每个元素的列表。这在大多数情况下都有效,但对于只有少数元素具有伪元素的大型文档,这可能会很慢。
在这种情况下,我们可以从不同的角度来处理这个问题。首先,我们循环遍历文档样式表,并构建一个与before
或after
伪元素相关联的选择器字典:
function getPseudoElementSelectors() {
var matchPseudoSelector = /:{1,2}(after|before)/,
found = { before: [], after: [] };
if (!(document.styleSheets && document.styleSheets.length)) return found;
return Array.from(document.styleSheets)
.reduce(function(pseudoSelectors, sheet) {
try {
if (!sheet.cssRules) return pseudoSelectors;
// Get an array of all individual selectors.
var ruleSelectors = Array.from(sheet.cssRules)
.reduce(function(selectors, rule) {
return (rule && rule.selectorText)
? selectors.concat(rule.selectorText.split(','))
: selectors;
}, []);
// Construct a dictionary of rules with pseudo-elements.
var rulePseudoSelectors = ruleSelectors.reduce(function(selectors, selector) {
// Check if this selector has a pseudo-element.
if (matchPseudoSelector.test(selector)) {
var pseudoElement = matchPseudoSelector.exec(selector)[1],
cleanSelector = selector.replace(matchPseudoSelector, '').trim();
selectors[pseudoElement].push(cleanSelector);
}
return selectors;
}, { before: [], after: [] });
pseudoSelectors.before = pseudoSelectors.before.concat(rulePseudoSelectors.before);
pseudoSelectors.after = pseudoSelectors.after.concat(rulePseudoSelectors.after);
// Quietly handle errors from accessing cross-origin stylesheets.
} catch (e) { if (console && console.warn) console.warn(e); }
return pseudoSelectors;
}, found);
}
我们可以使用这个字典来获得在匹配这些选择器的元素上定义的伪元素数组:
function getPseudoElements() {
var selectors = getPseudoElementSelectors(),
names = ['before', 'after']
return names.reduce(function(pseudoElements, name) {
if (!selectors[name].length) return pseudoElements;
var selector = selectors[name].join(','),
elements = Array.from(document.querySelectorAll(selector));
return pseudoElements.concat(
elements.reduce(function(withContent, el) {
var pseudo = getComputedStyle(el, name);
// Add to array if element has content defined.
return (pseudo.content.length)
? withContent.concat(pseudo)
: withContent;
}, [])
);
}, []);
}
最后,我用了一个小实用函数将大多数DOM方法返回的类似数组的对象转换为实际数组:
Array.from = Array.from || function(arrayish) {
return [].slice.call(arrayish);
};
等等调用getPseudoElements()
返回与文档中定义的伪元素相对应的CSS样式声明数组,而无需遍历和检查每个元素。
jsFiddle演示
注意事项
如果希望这种做法能说明一切,那就太过分了。有几件事需要记住:
- 它只返回
before
和after
伪元素,尽管很容易将其调整为包括其他元素,甚至是可配置列表 - 没有适当CORS头的跨域样式表将引发(抑制的)安全异常,并且不会被包括在内
- 只有在CSS中设置的伪元素才会被拾取;那些直接在JavaScript中设置的不会
- 一些奇怪的选择器(例如,像
li[data-separator=","]:after
这样的选择器)会被破坏,尽管我很确定我可以通过一点工作使脚本抵御大多数选择器
性能
性能会有所不同,这取决于样式表中规则的数量以及与定义伪元素的选择器匹配的元素的数量。如果您有大的样式表、相对较小的文档或具有伪元素的元素比例较高,Max K的解决方案可能会更快。
我在几个网站上测试了一下,以了解不同情况下的性能差异。以下是在控制台(Chrome 31)中循环运行每个功能1000次的结果:
- 谷歌(英国)
getPseudoElementsByCssSelectors
:757msgetPseudoElements
:1071ms
- 雅虎!英国
getPseudoElementsByCssSelectors
:59msgetPseudoElements
:5492ms
- MSN-UK
getPseudoElementsByCssSelectors
:341msgetPseudoElements
:12752ms
- 堆栈溢出
getPseudoElementsByCssSelectors
:22msgetPseudoElements
:10908ms
- Gmail
getPseudoElementsByCssSelectors
:42910msgetPseudoElements
:11684ms
- Nicholas Gallagher的纯CSS GUI图标演示
getPseudoElementsByCssSelectors
:2761msgetPseudoElements
:948ms
用于测试性能的代码
请注意,在最后两个例子中,MaxK的解决方案胜过了我的解决方案。我本以为它会出现在Nicholas Gallagher的CSS图标页面上,但不是Gmail!事实证明,Gmail总共有近110个选择器,在5个样式表中指定伪元素,总共有9600多个选择器,这使实际使用的元素数量(约2800个)相形见绌。
值得注意的是,即使在最慢的情况下,Max的解决方案运行一次也不需要超过10ms,考虑到它的长度是我的四分之一,而且没有任何警告,这还不错。
Max K共享了一个解决方案,在该解决方案中,所有元素都会检查其计算样式,这是我自己在最后一天已经将其用作临时解决方案的概念。巨大的缺点是性能开销,因为所有元素都要检查两次不存在的伪元素的计算样式(我的脚本执行时间是原来的两倍,因为不可能有伪元素可用)。
不管怎样,我只是想分享一下我在过去几天里使用的稍微通用一点的版本
var loopOverAllStyles = function(container,cb){
var hasPseudo = function(el){
var cs;
return {
after: (cs = getComputedStyle(el,"after"))["content"].length ? csa : false,
before: (cs = getComputedStyle(el,"before"))["content"].length ? csb : false
};
}
var allElements = container.querySelectorAll("*");
for(var i=0;i<allElements.length;i++){
cb(allElements[i],"element",getComputedStyle(allElements[i]));
var pcs = hasPseudo(allElements[i]);
if(pcs.after) cb(allElements[i],"after",pcs.after);
if(pcs.before) cb(allElements[i],"before",pcs.before);
}
}
loopOverAllStyles(document,function(el,type,computedStyle){
console.log(arguments);
});
- 查找元素高度,包括边距
- 无法覆盖CSS伪元素:before
- 如何将页面上的所有电话号码更改为可点击链接,不包括特定类别的元素
- spin.js/angular spinner:如何将spin定位到DOM元素中(包括plunker演示)
- 使用JQuery修改伪元素的css
- 如何使用Jquery删除伪元素
- 将伪元素添加到<文本区域>
- 伪元素上的过渡结束未在 Edge 上触发
- 如何在不丢失格式的情况下连续淡入() 元素文本的每个字符,包括嵌套元素
- 伪元素和旋转列表
- 伪元素适用于javascript
- 让用户使用鼠标选择伪元素
- 获取包装's的innerHTML,包括用户在输入元素中输入的值
- 包括滚动的元素偏移
- 如何选择html5范围's在JavaScript中的伪元素
- 如何循环页面上的所有元素,包括伪元素
- Aurelia的需求元素包括内联的外部资源
- 如何使用css悬停在另一个元素和伪类::之前的叠加
- 窗口.getMatchedCSSRules所有规则,包括它的伪元素和伪类规则
- 在纯Javascript中查询元素的伪类