2

I am trying to move an element from the light DOM to the shadow DOM, but when I do so the styling isn't copying over. I tried to fix this by setting the newElement.style = window.getComputedStyle(elem), but this hasn't seemed to work. The styles should be:

.card {
    color: #ff0;
    font-size: 3rem;
    font-weight: 600;
    border: 3px solid blueviolet;
    background-color: greenyellow;
}

but the styles don't apply and when I print the getComputedStyle() to console what I see is: all the values are empty

However, when I loop through the properties of getComputedStyle() with .getPropertyValue() like so:

for(let property of style){
    console.log(`property: ${property}, value: ${style.getPropertyValue(property)}`);
}

what I get in the console is: the correct values

So I'm confused as to why getComputedStyle() doesn't contain the values, but using getComputedStyle().getPropertyValue() returns the correct values. I'm sure I'm missing something obvious, as I couldn't find another post about this anywhere.

Any help would be greatly appreciated, thanks in advance.

EDIT: I've taken the code provided by Danny below and modified it to better show the issue I'm facing:

<style>
  .card {
    color: yellow;
    background: green;
  }
</style>

<my-element>
  <div class="card">lightDOM reflected to shadowDOM</div>
</my-element>

<script>
  customElements.define("my-element", class extends HTMLElement {
    constructor(){
        super().attachShadow({mode:"open"}).innerHTML = ``;
    }
    connectedCallback() {
      setTimeout(() => { // wait till innerHTML is parsed
        let card = this.children[0]; // Get the light DOM Card element
        this.shadowRoot.appendChild(card.cloneNode(true)); // Append it to the shadowDOM
        let style = window.getComputedStyle(card); // Get style of the Light DOM Card
        this.shadowRoot.querySelector('.card').style = style; // Set the ShadowDOM card style equal to the Light DOM Style
        console.log(style);
        console.log(style.color);      // yellow = rgb:255,255,0
        console.log(style.background); // green  = rgb:0,128,0
        card.remove(); // Remove the card from the Light DOM to prevent duplication
      })
    }
  })
</script>

Notice that the styling above doesn't apply even though it seems to be exactly as the docs specify: "The returned object is the same CSSStyleDeclaration type as the object returned from the element's style property. However, the two objects have different purposes:

ADSmith-0
  • 35
  • 1
  • 7
  • Global CSS does not style shadowDOM, that is the main purpose of shadowDOM. If you have a separate style tag in the DOM you can move or copy that to shadowDOM – Danny '365CSI' Engelman Aug 16 '21 at 16:12
  • @Danny'365CSI'Engelman "you can move or copy that to shadowDOM", yeah I'm trying to do that, but programmatically rather than manually. Which is where I'm running into the problem where the light DOM element's `getComputedStyle()` is blank for some reason – ADSmith-0 Aug 17 '21 at 09:21

2 Answers2

1

From MDN Documentation:

The Window.getComputedStyle() method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain. Individual CSS property values are accessed through APIs provided by the object, or by indexing with CSS property names.

It's stated that you need to use API functions, such as getPropertyValue() to get the value of it.

Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle

If you want to print all of the CSS styles from a specific element you may just iterate all the attributes like this:

function dumpCSSText(element){
  var s = '';
  var o = getComputedStyle(element);
  for(var i = 0; i < o.length; i++){
    s+=o[i] + ': ' + o.getPropertyValue(o[i])+';\n';
  }
  return s;
}

var e = document.querySelector('.card');
console.log(dumpCSSText(e));
.card {
    color: #ff0;
    font-size: 3rem;
    font-weight: 600;
    border: 3px solid blueviolet;
    background-color: greenyellow;
}
<div class="card"></div>
Dhana D.
  • 1,670
  • 3
  • 9
  • 33
  • Yes it states "**Individual CSS property values** are accessed through APIs provided by the object, or by indexing with CSS property names.", but I was hoping to copy over the entire sheet. From the [docs you linked](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle#description): "The returned object is the same CSSStyleDeclaration type as the object returned from the element's style property." So I should be able to do `shadow_elem.style = window.getComputedStyle(light_elem)` right? But for some reason the light DOM element's `getComputedStyle()` is empty. – ADSmith-0 Aug 17 '21 at 09:16
  • @ADSmith-0 You need to access all the attributes one-by-one by iterating through it as you can't print whole `getComputedStyle()` at once. – Dhana D. Aug 17 '21 at 10:22
  • If it is a STYLE element, and you do not want to move it with appendChild, you can also grab its innerHTML – Danny '365CSI' Engelman Aug 17 '21 at 16:26
0
  1. property style is read-only so you can't assign anything to it;
    (I stand corrected per comments; you can assign a value, but it will override all values)

  2. The innerHTML of Custom Elements is not parsed yet when the connectedCallback fires. So getting styles of its children with getComputedStyle is an operation on non-existing elements.

  3. If you reflect the lightDOM contents to a <slot> in shadowDOM, there is no need to copy styles as the styling from lightDOM is reflected

<style>
  .card {
    color: yellow;
    background: green;
  }
</style>

<my-element>
  <div class="card">lightDOM reflected to shadowDOM</div>
</my-element>

<script>
  customElements.define("my-element", class extends HTMLElement {
    constructor(){
        super().attachShadow({mode:"open"}).innerHTML = `<slot></slot>`
    }
    connectedCallback() {
      setTimeout(() => { // wait till innerHTML is parsed
        let card = this.querySelector(".card"); // in lightDOM!
        let style = window.getComputedStyle(card);
        console.log(style.color);      // yellow = rgb:255,255,0
        console.log(style.background); // green  = rgb:0,128,0
      })
    }
  })
</script>

More reading:

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • "property style is read-only so you can't assign anything to it" That's not true, it is a `PutForwards=cssText` https://drafts.csswg.org/cssom/#elementcssinlinestyle which means setting this property will set the `.cssText` of the actual `CSSStyleDeclaration`, which is exactly how OP was trying to use it. – Kaiido Aug 21 '21 at 08:45