skip to Main Content

There seems to be an oddity in isolating Shadow DOM styles from the Main DOM styles, see the below.

In essence if I am specific about the tagName then there is isolation, but if I use * as the CSS selector then the style flows into the Shadow DOM. The issue here is that the Main DOM is using a style library which appears to do this, I’m not sure if there are other cases of the main DOM styling overflowing into the Shadow DOM, but this clearly is and everything I’ve read about Shadow DOM suggests there is full isolation.

class myTest extends HTMLElement {
    constructor () {
        super(); 
    }

    connectedCallback () {
        const shadow = this.attachShadow({ mode: 'closed' });
        const h1 = document.createElement('h1')
              h1.innerHTML = 'In Shadow DOM'
              shadow.appendChild(h1)
    }
   
}

customElements.define('my-test', myTest);
h1 { color: red }
* { 
  font-family: sans-serif
}
<h1>In main DOM</h1>
<my-test></my-test>

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @Danny '365CSI' Engelman is seems it expected that the isolation is not complete, so I've looked at the link @Danny sent and edited the snipped to show what might be done if you get this problem.

    class myTest extends HTMLElement {
        constructor () {
            super(); 
        }
    
        connectedCallback () {
            const shadow = this.attachShadow({ mode: 'closed' });
            const h1 = document.createElement('h1')
                  h1.innerHTML = 'In Shadow DOM'
                  shadow.appendChild(h1)
                  
            // use * { all: initial } to reset. But you need to make sure <style>
            // is :not reset ( for some reason it get displayed otherwise )
            // also seems to change the h1 font size.
            
            const style = document.createElement('style')
                  style.textContent = `*:not(style) { all: initial }`
                  shadow.appendChild(style)
        }
       
    }
    
    customElements.define('my-test', myTest);
    h1 { color: red }
    * { 
      font-family: sans-serif
    }
    <h1>In main DOM</h1>
    <my-test></my-test>


  2. Re: other answer

    the connectedCallback will run multiple times when the Custom Element is moved in the DOM.

    The constructor is better for creating all shadowDOM; it will execute only once

    There hardly ever is a need for a closed shadowRoot, it has nothing to do with styling.
    So an open shadowRoot is fine; returning this.shadowRoot, thus can be chained.

    customElements.define('my-test', class extends HTMLElement {
      constructor() {
        const element = (tag,props) => Object.assign( document.createElement(tag) , props );
        super() // sets and returns 'this'
          .attachShadow({mode: 'open'}) // sets and returns this.shadowRoot
          .append(
            element("H1", {
              innerHTML : 'In Shadow DOM'
            }),
            element("STYLE", {
              innerHTML : `*:not(style) { all: initial }`
            })
          );
      }
    });
    h1 {
      color: red
    }
    
    * {
      font-family: sans-serif
    }
    <h1>In main DOM</h1>
    <my-test></my-test>

    Note: using innerHTML for setting <style> (text)content; makes for better GZIP/Brotli compression… we want small components after all.

    Live edit <style>

    <style> (and <script>) content are HTML, and can be displayed as HTML content.

    The style is still applied, run below code and edit red to any HTML color you know

    <h1>Hello World!</h1>
    
    <style contenteditable style="display:block">
      h1 { color: red }
    </style>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search