【问题标题】:Shared styling with custom HTML elements?与自定义 HTML 元素共享样式?
【发布时间】:2019-04-04 23:50:32
【问题描述】:

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

我考虑过的选项是:

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

  2. 据我从聚合物的文档 [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>,也会有问题。

现在,我认为#6 可能是最好的,因为它似乎是最通用/最容易使用的,无需专门为它构建,而且在实施时它会使过渡到 #5 变得微不足道。 但我想知道是否有其他方法或建议?

[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>

【问题讨论】:

  • 只是想知道:跨浏览器功能对您来说有多重要?
  • 好点,我忘了说,对于我的用例,我只关心最新版本的 chrome。

标签: javascript html css custom-element


【解决方案1】:

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

现在您也可以在自定义元素 Shadow DOM 中使用 &lt;link rel="stylesheet"&gt;

<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>

【讨论】:

  • 这似乎等同于#7,并遵循相同的缺点;例如如果 element-2 由依赖项定义,我将无法使用此方法设置其按钮的样式。但我实际上是在一个项目上尝试这个(所有元素都是我的),它工作正常。
  • 我不明白你所说的依赖,第三方自定义元素是什么意思?是的,这是一个限制,但这正是 shadow dom 功能的目的和预期行为。
  • 是的,这就是我所说的依赖,虽然它可能不是第 3 方,而是你自己的通用元素包,你想用不同的样式重用。我不同意这是“目的和预期行为”,它更多的是实施滞后。过去肯定有人尝试使用阴影元素来支持这一点(例如 /deep/、:shadow 和 @apply),并且肯定有计划在未来支持这一点(例如 ::part 和 ::theme),目前正在积极至少在 chrome 上工作过。
  • 不同的尝试因多种原因而失败(缓慢、无法实施、危险......)。 ::part 和 ::theme 不能与 3rd 方库一起使用,并且不会很快(仍然没有 alpha 版本)。自定义属性(或自定义实现)是唯一的选择(具有与答案相同或更多的限制)。这就是为什么说 CSS 隔离是 Shadow DOM 的目的是现实的。如果您想要全局样式,我建议您设计没有 Shadow DOM 的自定义元素。它们也很好用,并且更容易填充。
  • 有趣的建议,我在网上发现很少有使用没有影子 dom 的自定义元素的资源,所以我认为这是不好的做法之类的。我会尝试一下我的用例,看看是否出现任何问题。
【解决方案2】:

如果你使用 css,你可以这样做:

button {

  /* Put Style Here */  

}

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

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

【讨论】:

  • 全局样式不会应用在 Shadow DOM 中
猜你喜欢
  • 2015-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多