如何遍历页面上包含伪元素的所有元素?

Dav*_*der 11 javascript css

我如何循环遍历所有元素,包括伪元素?我知道我可以使用它getComputedStyle(element,pseudoEl)来获取它的内容,但是我一直无法找到一种方法来获取页面上的所有伪元素,以便我可以使用前面提到的函数来获取它们的内容/样式.似乎是一个简单的问题,但一直无法找到任何解决方案.

Max*_*x K 5

你走在正确的轨道上.遍历所有的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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Jor*_*ray 5

经过一些性能测试后,我的建议是:

  • 大多数情况下,请使用Max K的解决方案。在大多数情况下,该性能足够好,可靠,时钟频率低于15 LOC(最小值约为70)。
  • 如果您确实需要每隔一毫秒挤出一次,并且您知道(因为您已经对其进行了实际测试),请使用以下解决方案,它的速度更快。

(通常)更快的解决方案

您已经知道如何使用来获取文档中每个元素的列表document.querySelectorAll('*')。这在大多数情况下都可行,但是对于只有少数元素具有伪元素的较大文档,它可能会很慢。

在这种情况下,我们可以从另一个角度解决问题。首先,我们遍历文档样式表,并构造一个与beforeafter伪元素关联的选择器字典:

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);
}
Run Code Online (Sandbox Code Playgroud)

我们可以使用此字典来获取在与那些选择器匹配的元素上定义的伪元素数组:

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;
            }, [])
        );
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

最后,我使用了一个实用工具功能,将大多数DOM方法返回的类似数组的对象转换为实际的数组:

Array.from = Array.from || function(arrayish) {
    return [].slice.call(arrayish);
};
Run Code Online (Sandbox Code Playgroud)

等等!调用getPseudoElements()返回与文档中定义的伪元素相对应的CSS样式声明的数组,而无需循环遍历和检查每个元素。

jsFiddle演示

注意事项

希望这种方法能够解决所有问题,实在是太过分了。有几件事要牢记:

  • 它仅返回beforeafter伪元素,尽管很容易使其适应其他元素,甚至可配置列表。
  • 没有适当的CORS标头的跨域样式表将引发(抑制)安全例外,并且不会包含在内。
  • 只有在CSS中设置的伪元素才会被提取;那些直接在JavaScript中设置的功能则不会。
  • 一些奇怪的选择器(例如,类似li[data-separator=","]:after)将被破坏,尽管我很确定我可以通过一些工作就能使大多数脚本防弹。

性能

性能将根据样式表中的规则数量以及与定义伪元素的选择器匹配的元素数量而有所不同。如果您的样式表很大,文档相对较小或带有伪元素的元素比例较高,那么Max K的解决方案可能会更快。

我在几个站点上对此进行了一些测试,以了解不同情况下的性能差异。以下是在控制台(Chrome 31)中循环运行每个功能1000次的结果:

  • Google(英国)
    • getPseudoElementsByCssSelectors:757毫秒
    • getPseudoElements:1071ms
  • 雅虎!英国
    • getPseudoElementsByCssSelectors:59毫秒
    • getPseudoElements:5492ms
  • MSN英国
    • getPseudoElementsByCssSelectors:341ms
    • getPseudoElements:12752ms
  • 堆栈溢出
    • getPseudoElementsByCssSelectors:22毫秒
    • getPseudoElements:10908ms
  • 邮箱
    • getPseudoElementsByCssSelectors:42910ms
    • getPseudoElements:11684ms
  • Nicholas Gallagher的纯CSS GUI图标演示
    • getPseudoElementsByCssSelectors:2761ms
    • getPseudoElements:948ms

用于测试性能的代码

注意,在最后两个示例中,Max K的解决方案使我脱颖而出。我期望Nicholas Gallagher的CSS图标页面能够实现,但Gmail却无法实现!事实证明,Gmail总共有将近110个选择器,这些选择器在5个样式表中指定伪元素,总共有9600多个选择器,这使使用的实际元素数量(约2800个)相形见war。

值得注意的是,即使在最慢的情况下,Max的解决方案一次运行也不会花费超过10毫秒的时间,考虑到它只有我的长度的四分之一并且没有任何警告,这也不错。