与自定义HTML元素共享样式吗?

时间:2018-10-31 18:13:59

标签: javascript html css custom-element

我开始使用自定义元素,而我不知道的一件事就是共享样式。例如,如果我有2个自定义元素<element-1><element-2>,它们都包含<button>,并且我希望所有按钮都具有某种样式,例如font-size:20px

我考虑的选项是:

  1. 在自定义元素中使用<stylized-button>自定义元素而不是<button>。在外部采购<element-1>时,这是有问题的。如果您还希望仅在color:red按钮上而不是<element-1>按钮上进行其他样式设置(例如<element-2>),也会引起问题。

  2. 据我从Polymer的文档[1]得知,聚合物对此也没有解决方案。

  3. /dead/:shadow似乎很有希望,但不再受支持。

  4. 类似@apply [2]似乎很有希望,但是该提案被撤回了。

  5. ::part::theme [3]似乎更有希望,但尚未得到支持。

  6. 使用js支持::part::theme [4]。我想如果不解决所有问题,这将非常脆弱。

  7. 将共享样式明确添加到每个自定义元素。

    class Element1 extends HTMLElement {
        constructor() {
            this.shadowRoot.addElement(sharedStyle);
        }
    }
    

    这似乎是非常严格的&手动的。还可能会影响性能吗?如果您从外部采购<element-1>,也会引起问题。

现在,我认为#5可能是最好的,因为它似乎是最通用/最容易使用的,而无需专门为其构建,此外,它在实施时过渡到#4的过程很简单。 但是我想知道是否还有其他方法或建议?

[1] https://www.polymer-project.org/3.0/docs/devguide/style-shadow-dom

[2] http://tabatkins.github.io/specs/css-apply-rule/

[3] https://meowni.ca/posts/part-theme-explainer/

[4]一个简单的实现和使用它的示例:https://gist.github.com/mahhov/cbb27fcdde4ad45715d2df3b3ce7be40

实现:

document.addEventListener('DOMContentLoaded', () => {
    // create style sheets for each shadow root to which we will later add rules
    let shadowRootsStyleSheets = [...document.querySelectorAll('*')]
        .filter(element => element.shadowRoot)
        .map(element => element.shadowRoot)
        .map(shadowRoot => {
          shadowRoot.appendChild(document.createElement('style'));
          return shadowRoot.styleSheets[0];
        });

    // iterate all style rules in the document searching for `.theme` and `.part` in the selectors.
    [...document.styleSheets]
        .flatMap(styleSheet => [...styleSheet.rules])
        .forEach(rule => {
          let styleText = rule.cssText.match(/\{(.*)\}/)[1];

          let match;
          if (match = rule.selectorText.match(/\.theme\b(.*)/))
            shadowRootsStyleSheets.forEach(styleSheet => styleSheet.addRule(match[1], styleText));
          else if (match = rule.selectorText.match(/\.part\b(.*)/))
            shadowRootsStyleSheets.forEach(styleSheet => styleSheet.addRule(`[part=${match[1]}]`, styleText));
        });
  });

及其用法:

<style>
  .my-element.part line-green {
    border: 1px solid green;
    color: green;
  }

  .theme .line-orange {
    border: 1px solid orange;
    color: orange;
  }

  /*
    must use `.part` instead of `::part`, and `.theme` instead of `::theme`
    as the browser prunes out invalid css rules form the `StyleSheetList`'s. 
  */
</style>

<template id="my-template">
  <p part="line-green">green</p>
  <p class="line-orange">orange</p>
</template>

<my-element></my-element>

<script>
  customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({mode: 'open'});
      const template = document.getElementById('my-template').content.cloneNode(true);
      this.shadowRoot.appendChild(template);
    }
  });
</script>

2 个答案:

答案 0 :(得分:1)

您可以使用@import url将外部样式表导入到不同的自定义元素中。

或者,现在您还可以在自定义元素Shadow DOM中使用<link rel="stylesheet">

<template id="element-1">
  <style> 
      @import url( 'button-style.css' )
  </style>
  <button>B-1</button>
</template>

<template id="element-2">
  <link rel="stylesheet" href="button-style.css">
  <button>B-2</button>
</template>

答案 1 :(得分:0)

如果您使用的是CSS,则可以执行以下操作:

button {

  /* Put Style Here */  

}

您还必须在html的头部添加一个链接:

<link rel=“stylesheet” href=“the address”>