MutationObserver and current/computed CSS styles

Viewed 18590

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

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 backgroundImage during 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 setTimeout also 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.

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

2 Answers

I used https://github.com/keithclark/ComputedStyleObserver to observe the computed style "display" and make changes depending on its value. Here is the code: (in my case it was a Vue.js project)

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,
    };
},
Related