1

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>
Merlin11
  • 35
  • 5

1 Answers1

0

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.

Impirator
  • 1,393
  • 1
  • 12
  • 24
  • Thanks Pal. And yes, I was eventually planning to use some framework, i just wanted to get a taste of things by just using vanilla web-components. – Merlin11 Jul 23 '20 at 20:54
  • Funny, you want to load extra Framework code just to avoid using a technique yourself that delays code execution from the ``connectedCallback`` until the Eventloop is cleared; a same technique the Frameworks use *under the hood* as well. ``connectedCallback`` can access DOM in FireFox, just not (and correctly so) in Webkit based Browsers: https://stackoverflow.com/questions/61971919/wait-for-element-upgrade-in-connectedcallback-firefox-and-chromium-differences – Danny '365CSI' Engelman Jul 24 '20 at 07:43
  • Note: ``setTimeout(()=>{ code });`` is perfectly valid code and enough. No **0** required. Just like RAF (requestAnimationFrame) it waits until the Eventloop is empty (and thus your DOM exists). I haven't seen a project yet where RAF is actually better than setTimeout. If you do use really heavy animations, see: https://stackoverflow.com/questions/38709923/why-is-requestanimationframe-better-than-setinterval-or-settimeout) – Danny '365CSI' Engelman Jul 24 '20 at 07:51
  • @Danny'365CSI'Engelman, fair point about the extra framework code to do the same thing. It's definitely a subjective sense that the structure of a framework is nicer to work with. That superfluous second argument to `setTimeout` is a personal vestige of a codebase that expected explicit parameter passing as a safeguard and as a note of intent to other developers. I shouldn't saddle others with my own conditioned behavior though, so I'll make an edit. :) – Impirator Jul 24 '20 at 16:34
  • I saddle others with my opinion.. So don't edit anything. Libraries are tools, do use them if you understand what the tool does. Alas most tool users have no clue about the underlying technology. So did most of us in jQuery days... – Danny '365CSI' Engelman Jul 24 '20 at 17:48