如何将 link 作为插槽嵌入到自定义组件中?

How to embed a link in a custom component as a slot?

我正在尝试创建一个包含一些动画的简单组件。我为 link 的文本设置了名称属性。我为 link 的目的地设置了一个插槽,但模板不允许将 link 设置为插槽。我不确定接下来要尝试什么。

当我将鼠标悬停在 link 上时,它只显示 <slot name= 而不是实际的 URL。

const template = document.createElement('template');
template.innerHTML = `
  <style>
    .txt-link a {
      color: #005fec;
      font-weight: 700;
      font-size: 18px;
      text-decoration: none;
      position: relative;
    }
    .txt-link a:hover {
      text-decoration: none;
      cursor: pointer;
    }
    .txt-link a::before {
      content: "";
      position: absolute;
      bottom: -5px;
      left: 0;
      width: 0;
      height: 1px;
      background-color: #005fec;
      visibility: hidden;
      transition: all 0.3s ease-in-out 0s;
    }
    .txt-link a:hover::before {
      visibility: visible;
      width: 100%;
    }
    .txt-link.arrow-link img {
      height: 15px;
      position: relative;
      top: 2px;
    }
  </style>
  <div class="txt-link arrow-link">
    <a href="<slot name="destination" />"></a>
    <img alt="arrow right icon" class="learn-more-arrow" src="https://www.pikpng.com/pngl/m/73-734729_arrow-blue-right-transparent-blue-arrow-right-clipart.png" loading="lazy">
  </div>
`;

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

    this.showInfo = true;

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    this.shadowRoot.querySelector('a').innerText = this.getAttribute('name');
  }
}

window.customElements.define('custom-anim-link', CustomAnimLink);
<custom-anim-link name="Learn more">
  <div slot="destination">https://www.yahoo.com</div>
</custom-anim-link>

插槽不能做到这一点。插槽用于将 HTMLElement 放置在插槽的位置,不能用于除此之外的任何其他内容。

而是将 URL 作为属性传递。这似乎适合数据类型。 Web Components 有一些很好的生命周期来帮助你。

我添加了 static get observedAttributes() 以指示应遵守某些属性。这样做将使您能够在其中一个属性中的值发生更改时执行某些操作,例如更新 Shadow DOM.

中锚点上的 URL

只要观察到的属性值发生变化,就会调用 attributeChangedCallback,您可以在其中确定值发生变化时应该发生什么。

observedAttributesattributeChangedCallback 都是 Web 组件生命周期挂钩的一部分。

const template = document.createElement('template');
template.innerHTML = `
  <style>
    .txt-link a {
      color: #005fec;
      font-weight: 700;
      font-size: 18px;
      text-decoration: none;
      position: relative;
    }
    .txt-link a:hover {
      text-decoration: none;
      cursor: pointer;
    }
    .txt-link a::before {
      content: "";
      position: absolute;
      bottom: -5px;
      left: 0;
      width: 0;
      height: 1px;
      background-color: #005fec;
      visibility: hidden;
      transition: all 0.3s ease-in-out 0s;
    }
    .txt-link a:hover::before {
      visibility: visible;
      width: 100%;
    }
    .txt-link.arrow-link img {
      height: 15px;
      position: relative;
      top: 2px;
    }
  </style>
  <div class="txt-link arrow-link">
    <a href=""><slot></slot></a>
    <img alt="arrow right icon" class="learn-more-arrow" src="https://www.pikpng.com/pngl/m/73-734729_arrow-blue-right-transparent-blue-arrow-right-clipart.png" loading="lazy">
  </div>
`;

class CustomAnimLink extends HTMLElement {

  static get observedAttributes() {
    return ['name', 'destination'];
  }

  constructor() {
    super();

    this.showInfo = true;
    this.attachShadow({
      mode: 'open'
    });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  get anchor() {
    return this.shadowRoot.querySelector('a');
  }

  get name() {
    return this.getAttribute('name');
  }

  set name(value) {
    if ('string' === typeof value) {
      this.setAttribute('name', value);
    }
  }

  get destination() {
    return this.getAttribute('destination');
  }

  set destination(value) {
    if ('string' === typeof value) {
      this.setAttribute('destination', value);
    }
  }

  attributeChangedCallback(attrName, oldValue, newValue) {
    switch (attrName) {
      case 'destination':
        this.anchor.href = newValue;
        break;
      case 'name':
        this.anchor.textContent = newValue;
        break;
    }
  }
}

window.customElements.define('custom-anim-link', CustomAnimLink);
<custom-anim-link name="Learn more" destination="https://www.yahoo.com"></custom-anim-link>

或者,您可以在插槽的位置传递 <a> 标记。然后获取分配给 destination 插槽的所有元素,并根据 name 属性的值设置 textContent 属性。我们在上一个示例中添加的 destination 属性也是如此。

const template = document.createElement('template');
template.innerHTML = `
  <style>
    .txt-link a {
      color: #005fec;
      font-weight: 700;
      font-size: 18px;
      text-decoration: none;
      position: relative;
    }
    .txt-link a:hover {
      text-decoration: none;
      cursor: pointer;
    }
    .txt-link a::before {
      content: "";
      position: absolute;
      bottom: -5px;
      left: 0;
      width: 0;
      height: 1px;
      background-color: #005fec;
      visibility: hidden;
      transition: all 0.3s ease-in-out 0s;
    }
    .txt-link a:hover::before {
      visibility: visible;
      width: 100%;
    }
    .txt-link.arrow-link img {
      height: 15px;
      position: relative;
      top: 2px;
    }
  </style>
  <div class="txt-link arrow-link">
    <slot name="destination"></slot>
    <img alt="arrow right icon" class="learn-more-arrow" src="https://www.pikpng.com/pngl/m/73-734729_arrow-blue-right-transparent-blue-arrow-right-clipart.png" loading="lazy">
  </div>
`;

class CustomAnimLink extends HTMLElement {

  static get observedAttributes() {
    return ['name', 'destination'];
  }

  constructor() {
    super();

    this.showInfo = true;
    this.attachShadow({
      mode: 'open'
    });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  get destinations() {
    const slot = this.shadowRoot.querySelector('slot');
    const assignedElements = slot.assignedElements();
    return assignedElements;
  }

  get name() {
    return this.getAttribute('name');
  }

  set name(value) {
    if ('string' === typeof value) {
      this.setAttribute('name', value);
    }
  }
  
  get destination() {
    return this.getAttribute('destination');
  }

  set destination(value) {
    if ('string' === typeof value) {
      this.setAttribute('destination', value);
    }
  }
  
  attributeChangedCallback(attrName, oldValue, newValue) {
    switch(attrName) {
      case 'name':
        this.destinations.forEach(destination => {
          destination.textContent = newValue;
        });
        
        break;
      case 'destination':
        this.destinations.forEach(destination => {
          destination.href = newValue;
        });
        
        break;
    }
  }
}

window.customElements.define('custom-anim-link', CustomAnimLink);
<custom-anim-link name="Learn more" destination="https://www.yahoo.com">
  <a slot="destination"></a>
</custom-anim-link>

或者作为最后的简单手段,只需将 <a> 作为您的自定义元素的子项完整传递。

<custom-anim-link>
  <a href="https://www.yahoo.com">Learn more</a>
</custom-anim-link>