MutationObserver 和当前/计算的 CSS 样式

问题描述 投票:0回答:3

我正在使用 MutationObserver 来查找添加到网页的图像。由于许多图像是通过 CSS

background-image
属性显示的,因此除了查找
img
标签之外,我还检查当前/计算的 CSS 样式,例如...

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

但是,触发突变事件的节点与应用 CSS 规则之间似乎存在延迟,因此 在突变事件期间似乎没有

backgroundImage
,即使稍后某个时刻会有 。当代码在调试期间工作(使用断点)但在运行时不起作用时,我注意到了这一点。使用
setTimeout
延迟突变事件处理也有效,但可以肯定的是,延迟需要相当大,并且在不同页面之间会发生变化(我不确定何时保证应用 CSS 规则)。可能的原因可能只是 CSS 内容的延迟加载或注入。

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

javascript html css mutation-observers
3个回答
19
投票

这对我有用...

  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. 使用突变观察器捕获新的

    style
    link
    元素,添加
    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]);
            }
        }
    };
    

0
投票

我使用 https://github.com/keithclark/CompulatedStyleObserver 来观察计算的样式“显示”并根据其值进行更改。这是代码:(在我的例子中,它是一个 Vue.js 项目)

setup(){
    let observer = null;
    const collapsableItemsOnNavbar = ref(null);

    onMounted(() =>{            
        // This is to make sure that the Profile menu displays correctly between screen sizes
        const callback = entries => {
            entries.forEach(entry => {
                console.log(`Property '${entry.property}' changed from '${entry.previousValue}' to '${entry.value}'`);
                nextTick(() => {
                    let displayType = entry.value;
                    console.log(displayType);
                    if(displayType === 'none' || displayType === 'block'){
                        // We are in a small screen, don't display the profile picture
                        app.onNavbarCollapseChanged(true);
                    }
                    else{
                        // We are in a bigger screen, display the profile picture
                        app.onNavbarCollapseChanged(false);
                    }
                });
            });
        }

        observer = new ComputedStyleObserver(callback, ['display']);
        observer.observe(collapsableItemsOnNavbar.value);
    });
    onBeforeUnmount(() =>{
        observer.disconnect();
    });

    return {
        collapsableItemsOnNavbar,
    };
},

0
投票

我创建了一个包

@bramus/style-observer
以允许您执行此操作。

通过NPM安装:

npm i @bramus/style-observer

并像这样使用它:

import CSSStyleObserver from '@bramus/style-observer'

const cssStyleObserver = new CSSStyleObserver(
    /* Array of CSS Properties to observe */
    ['--variable1', '--variable2', 'display', 'border-width'],

    /* This is called whenever there is a change in one of the observed properties */
    (values) => {
        console.log(values);
    },
);

cssStyleObserver.attach(document.body); 

该库不依赖于

requestAnimationFrame
,而是通过在观察到的属性上声明一个非常短的转换来工作。为了满足不通过插值设置动画的属性(例如自定义属性),它还依赖于相当新的
transition-behavior: allow-discrete
。这种方法在图书馆的公告帖子中有详细介绍:https://brm.us/style-observer

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.