13

During development, there will still be situations where external CSS needs to be imported into Shadow DOM, so how to deal with it? The author gives the following schemes for the situation encountered recently.

One, @import

Sample code

const template = document.createElement('template');

class WhatsUp extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innderHTML = `
      <style>
          @import "./index.css"; // 核心代码
      </style>
      <div>Sup</div>
    `
  }
}

window.customElements.define('whats-up', WhatsUp);

Advantages: This method is very compatible, click to view caniuse .
Disadvantage: performance

Two,::part

::part CSS pseudo-element represents any element in the shadow tree that matches the attribute of part

Sample code

HTML

<template id="whats-up">
    <div part="sup">Sup</div>
    <div part="foo">Sup</div>
</template>

<whats-up></whats-up>

CSS

whats-up::part(sup) {
  /* 样式作用于 `sup` 部分 */
}
whats-up::part(foo) {
  /* 样式作用于 `foo` 部分 */
}

Advantages: concise and clear
Disadvantages: Compatibility is not very good, click to view caniuse .

Three, var

CSS custom properties can penetrate into Shadow DOM!
Sample code

JS

const template = document.createElement('template');
template.innerHTML = `
<style>
button {
  background: var(--background);
  color: var(--color);
  padding: var(--padding);
  font-size: var(--font-size);
  border: 0;
}
</style>
<div>Sup</div>`;

CSS

whats-up {
  --background: #1E88E5;
  --color: white;
  --padding: 2rem 4rem;
  --font-size: 1.5rem;
}

Advantages: good compatibility
Disadvantages: relatively limited, only a few can be set externally, and the style cannot be "flying freely"

Fourth, pass in through attributes

Sample code

JS

class Whatsup extends HTMLElement {
  static get observedAttributes() {return ['css']}

  constructor() {
    super();
  }

  get css() {
    return this.getAttribute('css');
  }

  set css(value) {
    if (value === null || value === false) {
      this.removeAttribute('css');
    } else {
      this.setAttribute('css', value);
    }
  }

  connectedCallback() {
    const shadowRoot = this.attachShadow({
      mode: 'open'
    });
    shadowRoot.innerHTML = `
    <style>
      :host{
        display: flex;
      }
      ${this.css} // 核心代码
      </style>
     <div class="name">Sup</div>
      `;
  }
}

HTML

<whats-up
  css="
    .name{
      color: red;
    }
  "
></whats-up>

Advantages: The style can be modified at will
Disadvantages: the code is not elegant enough

Five, custom component internal definition modification style function

Sample code

JS

class Whatsup extends HTMLElement {
  // ...

  // 核心代码
  reStyle(els, styles) {
    const elements = Array.isArray(els) ? els : [els];
    elements.forEach((element) => Object.assign(element.style, styles));
  }
}

HTML

<whats-up></whats-up>

<script>
    const myEle = document.querySelector('whats-up')
    const title = myEle.shadowRoot.querySelector('.title');

    myEle.reStyle(title, {
        color: 'red',
        width: '200px',
    })
</script>

Six, set the style through the slot outside

Sample code

JS

class WhatsUp extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = `
    <div>
      <slot name="header"></slot>
    </div>
    `;
  }
}
customElements.define('whats-up', WhatsUp);

HTML

<style>
  .header{
    color: red;
  }
</style>

<whats-up>
  <div slot="header" class="header">
    what's up
  </div>
</whats-up>

Seven, fetch acquisition

Sample code

class WhatsUp extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    // 获取样式
    fetch('./index.css').then(res => res.text()).then(data => {
        let node = document.createElement('style');
        node.innerHTML = data;
        this.shadowRoot.appendChild(node);
    });
    // ...
  }
}
customElements.define('whats-up', WhatsUp);

Advantages: The advantage is that the compatibility is good, the elements that support Shadow DOM all support this syntax; and the performance is OK
Disadvantages: not elegant

Eight, CSS module import

This method uses the browser's native import syntax, but CSS files are imported instead of JS files.
That is, the CSS file is directly imported as a module.
Sample code

import styles from "index.css";

class WhatsUp extends HTMLElement {
  constructor() {
    // ...
    // 核心代码
    shadow.adoptedStyleSheets = [styles];
  }
}

Advantages: The advantage is that it is convenient and fast to use and is the official recommended method, or the import CSS module is only supported for this scenario; and the performance is OK, the import itself is an asynchronous process.
Disadvantages: Poor compatibility, poke here caniuse .

Summarize

Various methods are applicable to different scenarios, so be careful to consume.


Allan91
2.3k 声望2.6k 粉丝