带有可选样式插槽容器的 Web 组件?

问题描述 投票:0回答:1

我相信 Web 组件插槽的原则之一是它们旨在模仿标准 html 期望。它们允许我们将元素投影到组件中,就像开发人员已经习惯使用其他元素(例如

select
option
元素和
ul
li
元素)一样。

然而,我看到的一个关键区别是默认情况下插槽似乎是强制性的 - 插槽元素始终出现在消费客户端浏览器中。当它或父级被设置样式时,这会导致问题,但当没有插槽时不应该出现问题,因为样式始终保留。

以我们有一个卡片 Web 组件的场景为例,该组件旨在将可选的标头部分实现为插槽。由于此标题部分需要不同的背景颜色,因此插槽的父级已应用相关样式。

我已经设法使标头可选,其中进行检查以计算插槽分配的节点的数量,但是为了满足与“标准”html元素的奇偶校验,我希望它在被CSS隐藏时也能做出反应。

我制作了一个 jsfiddle 示例来演示我的意思:https://jsfiddle.net/ajbrun/obLxdc9t/

您可以看到第一部分,我已成功根据标题槽的存在使标题部分显示或隐藏。然而,在其下面,我有一个类似的演示,该演示无法成功显示基于父类设置的可见性的标题。在每种情况下,左侧都有一个带有类似代码的标准 HTML 表示。

我想要实现的目标是可能的,还是 Web 组件的消费者的“期望”,他们应该完全删除 slot 元素以使其可选?

.cards {
  display: flex;
  flex-direction: row;
  gap: 10px;
}

.html-card,
.wc-card {
  width: 50%;
}

.card {
  border: 1px solid lightgray;
  padding: 10px;
}

.card .header {
  background: grey;
  padding: 10px;
}
<script type="module">
import {LitElement, html, css, styleMap} from 'https://cdn.jsdelivr.net/gh/lit/[email protected]/all/lit-all.min.js';

class CardComponent extends LitElement {
  static get styles() {
    return [css`
                 :host {
                    display:block;
                }
        
        
.card {
  border: 1px solid lightgray;
  padding: 10px;
}

.card .header {
  background: grey;
  padding: 10px;
}
            `];
  }
  
  static properties = {
    showHeader: {type: Boolean},
  };

  render() {
  const headerSlotStyles = { display: this.showHeader ? '' : 'none' };
    return html`
            <div class="card">
        <div class="header" style=${styleMap(headerSlotStyles)}>
            <slot name="header" @slotchange="${this.handleSlotChange}"></slot>
        </div>
        <slot></slot>
      </div>
        `;
  }

  handleSlotChange() {
  console.log('slot change');
        const headerSlot = this.shadowRoot.querySelector('slot[name=header]');
    this.showHeader = headerSlot.assignedNodes().length > 0;
  }
}

window.customElements.define('demo-card', CardComponent);

</script>

<script type="text/javascript">
  let htmlHeaderElemCopy, wcHeaderElemCopy;

  function toggleHeaderElement() {
    const htmlHeaderElem = document.querySelector('.slot-exists .header');
    const wcHeaderElem = document.querySelector('.slot-exists [slot=header]');
    if (!htmlHeaderElem) {
      document.querySelector('.slot-exists .card').prepend(htmlHeaderElemCopy);
      document.querySelector('.slot-exists .wc-card demo-card').prepend(wcHeaderElemCopy);
    } else {
      htmlHeaderElemCopy = htmlHeaderElem.cloneNode(true);
      wcHeaderElemCopy = wcHeaderElem.cloneNode(true);
      htmlHeaderElem.remove();
      wcHeaderElem.remove();
    }
  }

  function toggleParentClass() {
    const hasClass = document.querySelector('.parent-class.no-header');
    if (!hasClass) {
      document.querySelector('.parent-class').classList.add('no-header');
    } else {
      hasClass.classList.remove('no-header');
    }
  }

</script>

<style>
  .parent-class.no-header .header,
  .parent-class.no-header [slot=header] {
    display: none;
  }

</style>

<h1>
  Slot exists
  <button onclick="toggleHeaderElement()">
    Toggle header
  </button>
</h1>
<div class="cards slot-exists">
  <div class="html-card">
    <h2>
      Html card
    </h2>
    <div class="card">
      <div class="header">
        Card header
      </div>
      Content...
    </div>
  </div>
  <div class="wc-card">
    <h2>
      Web component card &#10004;
    </h2>
    <demo-card>
      <div slot="header">
        Card header
      </div>
      Content...
    </demo-card>
  </div>
</div>

<h1>
  Parent class toggled
  <button onclick="toggleParentClass()">
    Toggle header
  </button>
</h1>
<div class="cards parent-class">
  <div class="html-card">
    <h2>
      Html card
    </h2>
    <div class="card">
      <div class="header">
        Card header
      </div>
      Content...
    </div>
  </div>
  <div class="wc-card">
    <h2>
      Web component card &#10007;
    </h2>
    <demo-card>
      <div slot="header">
        Card header
      </div>
      Content...
    </demo-card>
  </div>
</div>

frontend web-component web-frontend lit
1个回答
0
投票

就像我在评论中所说,你有(作为最小的例子):

<div style="padding:10px"><slot></slot></div>

你总会看到那个填充:

<style id="STYLE">
  div {
    padding: 5px;
    background: hotpink;
    font-size: 10px;
  }
</style>

<div id="ONE">ONE</div><hr>
<div id="TWO"></div><hr>
<wc-number id="THREE"><div>THREE</div></wc-number><hr>
<wc-number id="FOUR">FOUR</wc-number><hr>
<wc-number id="FIVE"></wc-number>

<script>
  customElements.define("wc-number", class extends HTMLElement {
    constructor() {
      super().attachShadow({
        mode: "open"
      }).innerHTML = STYLE.outerHTML + `<div><slot></slot></div>`;
    }
  })
</script>

© www.soinside.com 2019 - 2024. All rights reserved.