在shadow DOM内部和外部使用非shadow DOM自定义元素

时间:2017-07-12 22:31:49

标签: html web-component shadow-dom custom-element

我有一个自定义元素(没有shadow DOM),我希望能够在任何地方使用,甚至在另一个可能使用shadow DOM的自定义元素中。但是,我不确定如何在两个地方都能使用这些样式。

例如,假设我创建了一个简单的fancy-button元素:

class fancyButton extends HTMLElement {
  constructor() {
    super();

    this.innerHTML = `
      <style>
      fancy-button button {
        padding: 10px 15px;
        background: rgb(62,118,194);
        color: white;
        border: none;
        border-radius: 4px
      }
      </style>
      <button>Click Me</button>`;
  }
}

customElements.define('fancy-button', fancyButton);
<fancy-button></fancy-button>

在shadow DOM元素中,插入的样式标记将允许fancy-button样式工作。但是,如果在shadow DOM元素之外使用此组件,则每次使用该元素时都会复制样式标记。

如果我将样式标记作为html导入文件的一部分添加,那么样式只能在影子DOM之外工作,但至少它们只被声明一次。

<!-- fancy-button.html -->
<style>
fancy-button button {
  padding: 10px 15px;
  background: rgb(62,118,194);
  color: white;
  border: none;
  border-radius: 4px
}
</style>

<script>
class fancyButton extends HTMLElement {
  constructor() {
    super();

    this.innerHTML = `<button>Click Me</button>`;
  }
}

customElements.define('fancy-button', fancyButton);
</script>

添加自定义元素样式以处理影子DOM内外使用的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

您可能希望将这些样式放在一个单独的CSS文件中,并与您的元素JS一起销售。但正如您已经指出的那样,如果您将元素放在另一个元素的Shadow DOM中,那么这些样式将无法在该范围内工作。出于这个原因,通常最好只创建一个阴影根并在那里弹出你的样式。你之所以不想这么做的原因吗?

答案 1 :(得分:1)

所以我能够找到一个解决方案,感谢Supersharp关于检查我们是否在影子DOM中的建议。

首先,您将样式添加为导入文件的一部分,以便默认情况下样式应用于shadow DOM之外。然后,当元素添加到DOM时,我们检查getRootNode()以查看它是否已添加到ShadowRoot节点。如果有,并且样式尚未注入根目录,那么我们可以手动注入样式。

var div = document.createElement('div');
var shadow = div.attachShadow({mode: 'open'});
shadow.innerHTML = '<fancy-button></fancy-button>';

document.body.appendChild(div);
<style data-fs-dialog>
  fancy-button button {
    padding: 10px 15px;
    background: rgb(62,118,194);
    color: white;
    border: none;
    border-radius: 4px
  }
</style>

<script>
class fancyButton extends HTMLElement {
  constructor() {
    super();
  }
  
  connectedCallback() {
    this.innerHTML = `<button>Click Me</button>`;
    
    var root = this.getRootNode();

    // In polyfilled browsers there is no shadow DOM so global styles still style
    // the "fake" shadow DOM. We need to test for truly native support so we know
    // when to inject styles into the shadow dom. The best way I've found to do that
    // is to test the toString output of a shadowroot since `instanceof ShadowRoot`
    // returns true when it's just a document-fragment in polyfilled browsers
    if (root.toString() === '[object ShadowRoot]' && !root.querySelector('style[data-fs-dialog]')) {
      var styles = document.querySelector('style[data-fs-dialog]').cloneNode(true);
      root.appendChild(styles);
    }
  }
}

customElements.define('fancy-button', fancyButton);
</script>

<fancy-button></fancy-button>

当所有浏览器都支持shadow DOM中的<link rel=stylesheet>时,内联脚本可以像robdodson建议的那样变成外部样式表,并且代码更清晰。