【问题标题】:html custom element make host css apply to shadow dom child insteadhtml自定义元素使主机css改为应用于shadow dom子
【发布时间】:2020-04-14 03:56:41
【问题描述】:

所以,我目前正在开发一个自定义元素,我希望以后能够像普通的 html 元素一样使用 css 设置样式。但是,我的元素的设置方式,一些 CSS 样式(如填充)在应用于元素本身时会破坏元素 - 相反,它们需要应用于自定义元素的 shadow dom 内的元素才能工作。所以我的问题是:如何让自定义元素上的用户定义 css 应用到其 shadow dom 中的元素?

为了说明这一点,假设在以下代码中使用了自定义元素:

<my-element style="padding:10px">Content here</my-element>

通常,我使用的影子 dom 会导致以下扁平 dom 树:

<my-element style="padding:10px">
    <div>
        Content here
    </div>
    Some other elements here
</my-element>

但我希望扁平化的 dom 树看起来像这样:

<my-element>
    <div style="padding:10px">
        Content here
    </div>
    Some other elements here
</my-element>

我已经尝试了许多解决方案,但到目前为止没有一个对我有用:

  • padding:inherit 应用于内部元素——这仅在我将填充也保留在父元素上时才有效,但我需要它为 0(或者以某种方式让子元素在仍然相对定位时忽略父填充,但我认为这是不可能的)
  • 使用 css 变量 --padding 设置内部元素的填充 - 这在技术上可行,但需要自定义元素的用户指定 --padding 而不仅仅是 padding,从而减少元素很好用
  • 使用脚本监听对填充的更改 - 据我所知,当元素的计算样式发生更改时无法获得通知,因此我必须定期检查以使其正常工作,可能减慢页面速度。例如,如果加载了一个影响自定义元素填充的新 css 表,则仅监听对 element.style 的更改是行不通的。

【问题讨论】:

  • 将全局样式表注入 shadowDOM 对您有用吗?在主 DOM 中禁用它:&lt;style id="injectMe" onload="this.disabled=true"&gt;....&lt;/style&gt;

标签: javascript html css shadow-dom custom-element


【解决方案1】:

您可以定义自定义属性。这是一个基本的例子

class CustomElement extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'closed' });
    const css = `
     :host {
       display:inline-block;
     }
      div {
       padding: ${this.getAttribute('padding')};
    }`
    this.styles = document.createElement('style');
    this.styles.innerHTML = css;
  }
  connectedCallback() {
    const div = document.createElement('div');
    div.innerHTML = this.innerHTML;
    this.shadow.appendChild(this.styles);
    this.shadow.appendChild(div);
  }
}

customElements.define('custom-element', CustomElement);
&lt;custom-element padding="10px"&gt;text here&lt;/custom-element&gt;

如果你想应用 CSS,或者像这样:

class CustomElement extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'closed' });
    const css = `
     :host {
       display:inline-block;
       padding:0!important;
     }
      div {
       padding: ${window.getComputedStyle(this,null).getPropertyValue("padding")}; 
    }`
    this.styles = document.createElement('style');
    this.styles.innerHTML = css;
  }
  connectedCallback() {
    const div = document.createElement('div');
    div.innerHTML = this.innerHTML;
    this.shadow.appendChild(this.styles);
    this.shadow.appendChild(div);
  }
}

customElements.define('custom-element', CustomElement);
custom-element {
  padding:10px;
}
&lt;custom-element &gt;text here&lt;/custom-element&gt;

【讨论】:

  • 我想这是一种方法,但它仍然不像仅使用 css 那样容易 - 例如,您必须在每个元素上指定它并且不能只使用 css 来选择所有该类型的元素。为代码示例 +1
  • 你必须在每个元素上指定它 -->这就是你的例子所展示的,你正在使用内联样式,所以你已经在这样做了
  • 我已经尝试过使用getComputedStyle,问题是我无法检测到计算样式何时发生变化。
【解决方案2】:

如果您只需要填充来允许用户控制并且您需要它不破坏布局,请指定 box-sizing...

host {
  box-sizing: border-box;
}

那么元素的用户就可以用传统方式调整padding而不用吹出来了……而且不需要添加额外的div。

【讨论】:

  • 这适用于不破坏元素的宽度和高度,但还有一些其他原因也不能直接在元素上使用填充。我不希望我的示例代码中的“其他一些元素”受到填充的影响,而且在我的情况下,外部元素是可滚动的,因此在它上面有一个填充在浏览器中的行为不一致。
【解决方案3】:

很简单。

对于CSS,只需设置

/* force to clear padding of the element */    
my-element { padding:0px !important; clear:both; } 

/* this indicate the first div found inside the element */
my-element > div:first-child { padding:10px; } 

就是这样。它应该可以工作。

【讨论】:

  • 我认为您还没有完全理解这个问题 - 当然我可以自己像这样向 div 添加填充,但我希望用户能够控制有多少填充,所以我正在寻找一种方法让 div 始终具有自定义元素上指定的填充。
  • 最佳做法是仅在其他所有操作都已完成但失败时才使用!important
猜你喜欢
  • 2020-02-14
  • 2020-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-23
  • 1970-01-01
相关资源
最近更新 更多