javascript MutationObserver 和当前/计算的 CSS 样式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20364687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 18:19:08  来源:igfitidea点击:

MutationObserver and current/computed CSS styles

javascripthtmlcssmutation-observers

提问by jozxyqk

I'm using a MutationObserver to look for added images to a web page. As many images are displayed via the CSS background-imageproperty, I'm checking the current/computed CSS style in addition to looking for imgtags, for example...

我正在使用 MutationObserver 来查找添加到网页的图像。由于许多图像是通过 CSSbackground-image属性显示的,因此除了查找img标签外,我还要检查当前/计算出的 CSS 样式,例如...

var hasBackgroundImage = function(node) {
    var nodeStyle = node.currentStyle || getComputedStyle(node, null);
    return (
        nodeStyle &&
        nodeStyle.backgroundImage &&
        nodeStyle.backgroundImage != "none"
        );
};

However, it seems there is a delay between a node triggering a mutation event and the CSS rules being applied so there appears to be no backgroundImageduring the mutation event even though there will be at some point later. I noticed this when the code worked during debugging (stepping with breakpoints) but didn't work at runtime. Delaying mutation event processing with setTimeoutalso works but to be certain the delay needs to be quite large and changes from page to page (I'm not sure exactly when the CSS rules are guaranteed to be applied). A possible cause may simply be the delayed load or injection of CSS content.

然而,似乎在触发突变事件的节点和正在应用的 CSS 规则之间存在延迟,因此在突变事件期间似乎没有backgroundImage,即使稍后会有。当代码在调试期间工作(使用断点步进)但在运行时不起作用时,我注意到了这一点。延迟突变事件处理setTimeout也可以工作,但要确定延迟需要很大并且从页面到页面变化(我不确定何时保证应用 CSS 规则)。一个可能的原因可能仅仅是 CSS 内容的延迟加载或注入。

What's the best way to achieve this functionality? I guess I'm after an on-style-change mutation, but I doubt this exists.

实现此功能的最佳方法是什么?我想我是在进行风格变化突变,但我怀疑这是否存在。

回答by jozxyqk

This works for me...

这对我有用...

  1. Use a mutation observer to catch changes to the style attribute...

    var observer = new MutationObserver(parseMutations);
    observer.observe(document, {
        ...
        attributes:    true,
        attributeFilter: ["style"]
    });
    
    ...
    
        if (mutation.attributeName) //we'll assume it's "style"
            parseNode(mutation.target); //check for style.backgroundImage and call filterNode()
    

    This works for bothsetAttribute("style", ...)and element.style.whatever = something.

  2. Catch new styleand linkelements with the mutation observer, add an onloadevent and parse applicable nodes...

    var stylenodes = ["STYLE", "LINK"];
    
    ...
    
    for (var i = 0; i < mutation.addedNodes.length; i++)
    {
        var node = mutation.addedNodes[i];
        var nodeName = node.nodeName.toUpperCase();
        if (stylenodes.indexOf(nodeName) !== -1)
            node.addEventListener("load", styleLoaded);
    
    ...
    
    //catch loading of stylenodes and parse all new rules
    var currentLoadedStyles = [];
    var styleLoaded = function() {
        //check all styles and look for one that has just added some rules
        for (var i = 0; i < document.styleSheets.length; ++i)
        {
            if (document.styleSheets[i].rules && document.styleSheets[i].rules.length > 0 && currentLoadedStyles.indexOf(document.styleSheets[i]) == -1)
            {
                currentLoadedStyles.push(document.styleSheets[i]);
                parseNewStyle(document.styleSheets[i].rules);
            }
        }
    };
    
    //look for rules with background images and re-filter all nodes it applies to
    var parseNewStyle = function(rules) {
        for (var i = 0; i < rules.length; ++i)
        {
            //if any rule contains a background-image (could look for anything here really)
            if (rules[i].style && rules[i].style.backgroundImage && rules[i].style.backgroundImage != "none")
            {
                //get all affected nodes and re-parse them
                var nodes = document.querySelectorAll(rules[i].selectorText);
                for (var j = 0; j < nodes.length; ++j)
                    filterNode(nodes[j]);
            }
        }
    };
    
  1. 使用突变观察器来捕捉对样式属性的更改...

    var observer = new MutationObserver(parseMutations);
    observer.observe(document, {
        ...
        attributes:    true,
        attributeFilter: ["style"]
    });
    
    ...
    
        if (mutation.attributeName) //we'll assume it's "style"
            parseNode(mutation.target); //check for style.backgroundImage and call filterNode()
    

    这适用于这两个setAttribute("style", ...)element.style.whatever = something

  2. 使用变异观察器捕获新元素stylelink元素,添加onload事件并解析适用的节点...

    var stylenodes = ["STYLE", "LINK"];
    
    ...
    
    for (var i = 0; i < mutation.addedNodes.length; i++)
    {
        var node = mutation.addedNodes[i];
        var nodeName = node.nodeName.toUpperCase();
        if (stylenodes.indexOf(nodeName) !== -1)
            node.addEventListener("load", styleLoaded);
    
    ...
    
    //catch loading of stylenodes and parse all new rules
    var currentLoadedStyles = [];
    var styleLoaded = function() {
        //check all styles and look for one that has just added some rules
        for (var i = 0; i < document.styleSheets.length; ++i)
        {
            if (document.styleSheets[i].rules && document.styleSheets[i].rules.length > 0 && currentLoadedStyles.indexOf(document.styleSheets[i]) == -1)
            {
                currentLoadedStyles.push(document.styleSheets[i]);
                parseNewStyle(document.styleSheets[i].rules);
            }
        }
    };
    
    //look for rules with background images and re-filter all nodes it applies to
    var parseNewStyle = function(rules) {
        for (var i = 0; i < rules.length; ++i)
        {
            //if any rule contains a background-image (could look for anything here really)
            if (rules[i].style && rules[i].style.backgroundImage && rules[i].style.backgroundImage != "none")
            {
                //get all affected nodes and re-parse them
                var nodes = document.querySelectorAll(rules[i].selectorText);
                for (var j = 0; j < nodes.length; ++j)
                    filterNode(nodes[j]);
            }
        }
    };