我如何循环遍历所有元素,包括伪元素?我知道我可以使用 getComputedStyle(element,pseudoEl)
得到它的内容,但是我一直无法找到一种方法来获取页面上的所有伪元素,以便我可以使用前面提到的函数来获取它们的内容/样式。似乎是一个简单的问题,但一直无法找到任何解决方案。
我如何循环遍历所有元素,包括伪元素?我知道我可以使用 getComputedStyle(element,pseudoEl)
得到它的内容,但是我一直无法找到一种方法来获取页面上的所有伪元素,以便我可以使用前面提到的函数来获取它们的内容/样式。似乎是一个简单的问题,但一直无法找到任何解决方案。
你走在正确的轨道上。循环使用所有DOM元素相当容易 getElementsByTagName("*")
要么 querySelectorAll("*")
。然后我们必须查看每个元素是否具有伪元素。所有这些都像@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);
}
}
经过一些性能测试,我的建议是:
您已经知道如何获取文档中每个元素的列表 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);
};
Etvoilà! 调用 getPseudoElements()
返回一个CSS样式声明数组,对应于文档中定义的伪元素,而不循环遍历并检查每个元素。
希望这种方法可以解决所有问题,这太过分了。有几点需要注意:
before
和 after
伪元素,虽然很容易使其适应包括其他元素,甚至是可配置列表。li[data-separator=","]:after
虽然我很确定我可以通过一些小工作来防止脚本对抗大多数这些,但是会被破坏。性能将根据样式表中的规则数量以及与定义伪元素的选择器匹配的元素数量而有所不同。如果您有大型样式表,相对较小的文档或具有伪元素的较高比例的元素, Max K's 解 可能会更快。
我在一些网站上对此进行了一些测试,以了解不同情况下的性能差异。以下是在控制台(Chrome 31)中循环运行每个函数1000次的结果:
getPseudoElementsByCssSelectors
:757毫秒getPseudoElements
:1071msgetPseudoElementsByCssSelectors
:59msgetPseudoElements
:5492msgetPseudoElementsByCssSelectors
:341毫秒getPseudoElements
:12752msgetPseudoElementsByCssSelectors
:22msgetPseudoElements
:10908msgetPseudoElementsByCssSelectors
:42910毫秒getPseudoElements
:11684msgetPseudoElementsByCssSelectors
:2761毫秒getPseudoElements
:948ms请注意,Max K的解决方案在最后两个例子中击败了我的裤子。我期待它与Nicholas Gallagher的CSS图标页面,但不是Gmail!事实证明,Gmail总共有近110个选择器,它们在5个样式表中指定了伪元素,总共超过9,600个选择器,这使得实际使用的元素数量相形见绌(大约2,800个)。
值得注意的是,即使在最慢的情况下,Max的解决方案仍然不需要花费超过10毫秒的时间来运行一次,这也不错,因为它是我的长度的四分之一并且没有任何警告。
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);
});