10

If I understand it correctly, creating an instance of a web component can be summed up as creating a shadow root and copying the markup, e.g. from a template into it:

var Template = document.querySelector('#myTemplate');
var TemplateClone = document.importNode(Template.content,true);
TargetElement.appendChild(TemplateClone);

Of course, if the template contains css rules in a style-tag, those will be copied as well. Thus we can have scoped styles which belong to the internal markup of a web component.

Questions:

  1. Does it have any performance implications when I create tons of instances of the very same web component, as the style is just copied and not reused?
  2. Is there a way to share the style node across multiple instances of the same web component?
Supersharp
  • 29,002
  • 9
  • 92
  • 134
Michael K
  • 1,070
  • 1
  • 10
  • 25
  • 2
    Just updated the answer to reflect a new way of declaring stylesheet for shadow DOM: https://stackoverflow.com/a/40984891/5723098 – Harshal Patil Feb 12 '19 at 04:57

1 Answers1

10

Does it have any performance implications...?

Yes, it depends on how many instances, and on the CSS engine implemented in the browser. You'll have to test every use case and take in account speed versus memory consumption.

Is there a way to share the style node across multiple instances of the same web component?

Yes, you can use @import url like in this SO question. Or you can choose to not use Shadow DOM and use global CSS style only.

2019 update

As Harshal Patil suggested, since Chrome 73 and Opera 60 it is possible for multiple Shadow DOM to adopt the same stylesheet. This way an update in the stylesheet will be applied to all the web components.

let css = new CSSStyleSheet
css.replaceSync( `div { color: red }` )

customElements.define( 'web-comp', class extends HTMLElement {
    constructor() {
        super()
        let shadow = this.attachShadow( { mode: 'open' } )
        shadow.innerHTML = `<div><slot></slot></div>`
        shadow.adoptedStyleSheets = [ css ]
    }
} )
color.oninput = () => css.replaceSync( `div { color: ${color.value} }` )
<web-comp>Hello</web-comp>
<web-comp>World</web-comp>
<input value=red id=color>
Supersharp
  • 29,002
  • 9
  • 92
  • 134
  • 1
    Isn't it still duplicated in markup, just with the difference, that it is loaded from an external url (with the result cached, of course)? – Michael K Mar 07 '17 at 12:27
  • 1
    Yes and no, the markup is not duplicated but a `stylesheet` object is added to the element's DOM. Anyways every element in the DOM is styled individually, so the more elements (and styles), the longer the styling process will be, whether the style is shared or not. I don't know the overhead of the replicated stylesheet but maybe it's not so much. And it should vary with the use case (and with the css engine implementation) – Supersharp Mar 07 '17 at 12:58
  • @MichaelK thank you for the accepted answer. You can upvote if it helped :-) – Supersharp Jan 31 '19 at 22:39
  • 1
    **important** Above code needs Chrome **73**. Which is [scheduled for official release](https://www.chromestatus.com/features/schedule) on **March 12 2019**, So you need to open this example in [Chrome Canary 'for Developers'](https://www.google.com/chrome/canary/) now (*it's feb 23 at time of writing*) – Danny '365CSI' Engelman Feb 23 '19 at 10:31
  • @Supersharp, may I suggest to change the ``onchange`` to ``oninput`` in the code example, gives more immediate feedback for HTML colornames – Danny '365CSI' Engelman Feb 23 '19 at 10:36
  • Running your snippet in latest Firefox gives me `"message": "TypeError: Illegal constructor.",` – connexo Jul 30 '19 at 08:32
  • Turns out css `@import url` does indeed build the full CSSOM. I would recommend a deploy-time process to minimize the css printed to the component to avoid excess RAM usage until adopted Stylesheets are a universal thing. – Simon Jan 03 '22 at 12:01