自定义 Web 组件,作用类似于 link(锚标记)

Custom web component that acts like a link (anchor tag)

我创建了一个自定义 Web 组件按钮,它具有 href 属性。如果单击按钮,我会使用 Javascript 导航到它自己的 href 属性。

不幸的是,在迁移了一个看起来像按钮的锚标记的大型代码库之后,我意识到我的自定义 Web 组件有很多缺点:

  1. 没有右键单击 link 上下文菜单。
    • 例如无法右键单击并 select 在选项卡中打开。
  2. 无法通过 CTRL+单击在新选项卡中打开 link。
  3. 无法在新标签页中单击鼠标中键打开。

项目 2-3 可以在 Javascript 中修复,但项目 1 并非微不足道。

我做了一些关于使用 is 关键字扩展内置元素的研究,但这只是让我陷入了 Safari 拒绝实施用于扩展 [=36 的 Web 组件规范的错误报告中=] 元素.

是否有任何方法可以将锚标记的行为添加到自定义 Web 组件,例如通过混合?

只需使用 @ungap/custom-elements polyfill,您就可以使用 Safari 甚至 Internet Explorer,它们都具有自定义的内置元素和自主的自定义元素。

那么一个实现可能是这样的:

class CustomAnchor extends HTMLAnchorElement {
  constructor() {
    super();
    this.addEventListener('click', this.click.bind(this));
  }
  
  click(event) {
    if (event.getModifierState('Control') || event.getModifierState('Meta')) return; // allow control-click or cmd-click (mac) to work as usual
    event?.preventDefault();
    console.log(this.href);
    // do whatever you like here
  }
}

customElements.define('custom-anchor', CustomAnchor, { extends: 'a' });
<script src="//unpkg.com/@ungap/custom-elements"></script>
<a is="custom-anchor" href="https://google.com">Custom google anchor</a>

扩展内置的缺点是没有允许样式封装的shadowDOM。如果您需要,请改为使用自主自定义元素并在内部使用本机锚标记(甚至您的扩展内置 HTMLAnchorElement):

const styles = `
  a { color: red; }
  a:hover { background-color: yellow; }
`;

class CustomAnchor extends HTMLElement {
  
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
    const style = document.createElement('style');
    style.textContent = styles;
    this.anchor = document.createElement('a');
    this.shadowRoot.append(style, this.anchor);
  }
  
  static get observedAttributes() { return ['link', 'text']; }  
  
  attributeChangedCallback(attr, oldVal, newVal) {
    if (oldVal === newVal) return; // nothing changed
    switch (attr) {
      case 'link':
        if (newVal) this.anchor.href = newVal;
        else this.anchor.removeAttribute('href');
        break;
      case 'text':
        this.anchor.textContent = newVal ?? '';
        break;
      default:
    }
  }
}

customElements.define('custom-anchor', CustomAnchor);
<script src="//unpkg.com/@ungap/custom-elements"></script>
<custom-anchor link="https://google.com" text="Google Search"></custom-anchor>

如果您更喜欢(例如出于屏幕阅读器和 SEO 原因)将 link 文本作为元素的内容,请输入默认值 <slot>:

const styles = `
  a { color: red; }
  a:hover { background-color: yellow; }
`;

class CustomAnchor extends HTMLElement {
  
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
    const style = document.createElement('style');
    style.textContent = styles;
    this.anchor = document.createElement('a');
    const slot = document.createElement('slot');
    this.anchor.appendChild(slot);
    this.shadowRoot.append(style, this.anchor);
  }
  
  static get observedAttributes() { return ['link']; }  
  
  attributeChangedCallback(attr, oldVal, newVal) {
    if (oldVal === newVal) return; // nothing changed
    switch (attr) {
      case 'link':
        if (newVal) this.anchor.href = newVal;
        else this.anchor.removeAttribute('href');
        break;
      default:
    }
  }
}

customElements.define('custom-anchor', CustomAnchor);
<script src="//unpkg.com/@ungap/custom-elements"></script>
<custom-anchor link="https://google.com">Google Search</custom-anchor>

只需包装一个 link 并将其设置为按钮。

点击代码片段中的link无效,因此我也为它创建了一个codepen。 https://codepen.io/waxolunist/pen/ZEXwQYa

<script type="module">
import {
  LitElement,
  html,
  css
} from "https://unpkg.com/lit-element/lit-element.js?module";

class MyLinkButton extends LitElement {
  
 static get properties() {
    return {
      href: { type: String, reflect: true },
    };
  }
  
  static get styles() {
    return css`
    .button {
      background-color: #4CAF50; /* Green */
      border: none;
      color: white;
      padding: 15px 32px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 16px;
    }

    .button:hover {background-color: #220440}

    .button:active {
        background-color: #230545;
        box-shadow: 0 5px #e1d5ed;
        transform: translateY(4px);
    }
    `;
  }

  render() {
    return html`
      <a class="button" href="${this.href}" target="_blank">
      <slot></slot>
      </a>
    `;
  }
}

customElements.define("my-linkbutton", MyLinkButton);
</script>
<my-linkbutton href="http://example.com">SOME TEXT</my-linkbutton>

抱歉,如果 unpkg 又慢了。