如何让light dom CSS应用于阴影dom元素?

时间:2019-04-17 13:07:32

标签: css dom shadow-dom

我定义了自己的自定义元素,并发现在阴影dom中添加其子元素非常方便,这主要是因为我可以在自定义元素构造函数中添加它,而完全相同的构造函数不允许我添加“常规”元素孩子们。

自定义元素的构造函数不允许您添加子对象,因为新元素在创建后就应该为空。

我想知道是否有一种简单的方法来使CSS和文档中的样式能够影响阴影dom中的元素。我希望轻型dom的选择器能够到达阴影dom中的这些元素。

我的自定义元素之一是“项目”。我将创建很多它们来填充列表。我不喜欢在每个项目实例的阴影dom中重复相同的样式标签的想法,因此我正在寻找一个放置所有我的项目的样式标签的地方。

我读了许多有关影子域以及内部样式如何不影响边界元素的文章,但是其中任何一个都回答了我的问题。

有什么想法吗?谢谢!

2 个答案:

答案 0 :(得分:2)

当前版本的shadowDOM要求您将CSS放在shadowDOM内。

大多数CSS都非常小,每个元素仅增加几个字节到几百个字节。我看到的一些最大的CSS副本每个副本中都添加了约2k的CSS。但这与DOM结构中表示的数据相比仍然很小。

有一些东西会从外面流失,​​例如字体信息,但是没有很多。

以下是一些可以从外部影响到的方法:

1。 CSS变量

CSS Variable允许您将变量设置为CSS内使用的值,无论是否在shadowDOM中。

2。属性

可以捕获属性并将其迁移到shadowDOM CSS中。我有一些使用属性定义主题的组件。

3。属性

还可以获取属性并将其应用于内部CSS。

讨论中还有其他方法,但是这些方法必须等到V2。

class MyEL extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'}).innerHTML = `
    <style>
    .outer {
      color: var(--myColor, 'black');
    </style>
    <div class="outer">
      <h4>Title</h4>
      <slot></slot>
    </div>`;
  }

  static get observedAttributes() {
    return ['bgcolor'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      this.shadowRoot.querySelector('.outer').style.backgroundColor = newVal;
    }
  }

  get border() {
    return this.shadowRoot.querySelector('.outer').style.border;
  }
  set border(value) {
    this.shadowRoot.querySelector('.outer').style.border = value;
  }
}

customElements.define('my-el', MyEL);

setTimeout(() => {
  document.querySelector('my-el').border = '2px dashed blue';
},1000);

const btn = document.getElementById('toggle');
let color = '';
btn.addEventListener('click', () => {
  color = color === '' ? 'white' : '';
  document.querySelector('my-el').style.setProperty('--myColor', color);
});
<my-el bgcolor="red"></my-el>
<hr/>
<button id="toggle">toggle</button>

答案 1 :(得分:0)

首先,我不知道您所说的自定义组件应该为空的意思是什么……我认为那不是真的,因为我找不到任何说明该要求的文档。此外,以下对我来说很好用:

customElements.define("my-element", class extends HTMLElement {
  constructor() {
    super();

    // Add a `span` element to the component's light-DOM.
    const span = document.createElement("span");
    span.innerHTML = "Hello, World!";
    this.appendChild(span);

    // Add a `slot` element to the component's shadow-DOM (so that the light-DOM gets rendered).
    this.attachShadow({ mode: "open" }).innerHTML = "<slot></slot>";
  }
});
<my-element></my-element>

如果您的意思是如果无法将light-DOM内容分配给shadow-DOM中的slot元素,那么您是正确的。

无论如何,那只是一个副节点。回到关于从Web组件外部设置阴影DOM样式的问题...

除了@Intervalia's quite thorough answer,您还可以查看一下Web组件的::part::theme伪元素。

类似这样的东西:

customElements.define("my-element", class extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: "open" }).innerHTML = `
      <style>
        * {
          position: relative;
          box-sizing: border-box;
          margin: 0;
          padding: 0;
        }
        :host {
          display: inline-block;
          height: 150px;
          width: 200px;
        }
        #outer {
          height: 100%;
          padding: 20px;
          background: lightblue;
          border: 5px solid blue;
        }
        #inner {
          height: 100%;
          background: lightgreen;
          border: 5px solid green;
        }
      </style>
      <div id="outer" part="foo">
        <div id="inner" part="bar">
        </div>
      </div>
    `;
  }
});
#second {
  width: 300px;
}

#second::part(bar) {
  background: lightcoral;
  border: 10px dotted red;
}
<my-element id="first"></my-element>
<my-element id="second"></my-element>

有关详细说明,我想参考:

请注意,::part伪元素和相应的part属性当前仅在Chrome和Opera中起作用。 FireFox将在版本69中支持它,但是首先您必须在其layout.css.shadow-parts.enabled页面中将true设置为about:config(这使我怀疑它对于使用Firefox的普通用户是否非常有用,所以我希望Mozilla会默认将其默认打开)。