Search code examples
javascriptweb-componentshadow-domgetcomputedstyle

unable to access styles with getComputedStyle in connectedCallback of webcomponent


I am fairly new to javascript and been playing around with web-components. I wanted to access the CSS color attribute of an element inside the shadow DOM of a web-component. When I use the getComputedStyle() method I am unable to access the style property on running it in the connectedCallback.

Here I am trying to access the color property, on logging the value to the console it shows RGB(0, 0, 0) whereas upon waiting for a millisecond and then logging, the correct value of RGB(0, 128, 0) shows up. Why is this happening?

I presume it is because the styles haven't been computed yet when I am running the function for the first time? What is a more elegant solution to this? How can I wait exactly till the styles have been computed to run my function, instead of some arbitrary time that I am specifying now?

JS

document.addEventListener('DOMContentLoaded',()=>{

    class CustomComponent extends HTMLElement{
        constructor(){
            super();

            this.attachShadow({mode:'open'});

            const template=document.querySelector('#component');
            this.shadowRoot.appendChild(template.content.cloneNode(true));
        };

        connectedCallback(){
            console.log('Element added to DOM');

            let text=this.shadowRoot.querySelector('.text');
            console.log(getComputedStyle(text).getPropertyValue('color'));
            setTimeout(()=>{console.log(getComputedStyle(text).getPropertyValue('color'))},1)
        };
    };

    customElements.define('custom-component',CustomComponent);
});

CSS

.container{
    --color-variable:#f2c846;
}

.text{
    color:green;
}
  

HTML

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title>Random title</title>
        <script src='./CustomComponent.js'></script>
    </head>
    <body>
        <template id='component'>
            <link rel='stylesheet' href='./CustomComponent.css'/>
            <div class=container>
                <div class='text'>
                    Colored Text
                </div>
            </div>
        </template>
        <custom-component>

        </custom-component>
    </body>
</html>

Solution

  • Oof, frustrating. I think your intuition here is probably right about styles not being immediately ready, and your instinct to wait is what I might go with as well.

    I don't think it's totally uncommon to see setTimeout(callback)—the second argument is superfluous in this case—as a signal that you're waiting for the event loop to run once (thus allowing the styles to compute). requestAnimationFrame(callback) might have the added advantage of signaling that you're waiting for some visual readiness as well, though it won't outperform setTimeout for this purpose. I'll be the first to say that I don't find these solutions as satisfying as a builtin callback or event to listen for, and I would've assumed connectedCallback was that thing.

    Additionally, if you don't mind using a framework (like Stencil) that facilitates working with Web Components, there would be callbacks provided by the framework that would let you know when the component was completely rendered.