StencilJS Web 组件:如何让最终用户通过自定义点击事件来防止默认?

StencilJS Web Component: How to allow end-user to prevent default via custom click event?

示例 Stencil.js 网络组件:

import { Component, ComponentInterface, Event, EventEmitter, h, Host } from "@stencil/core";

@Component({
    tag: 'foo-testwebcomponent'
})
export class TestWebComponent implements ComponentInterface {
    @Event({
        eventName: 'foo-click',
        cancelable: true
    }) fooClick: EventEmitter;
    fooClickHandler() {
        this.fooClick.emit();
    }

    render() {
        return(
            <Host>
                <a href="#"
                   onClick={this.fooClickHandler.bind(this)}
                >Testing</a>
            </Host>
        )
    }
}

HTML:

<foo-testwebcomponent id="test"></foo-testwebcomponent>

<script>
  document.addEventListener('DOMContentLoaded', () => {
    document.getElementById('test')
            .addEventListener('foo-click', event => {
              event.preventDefault();
              console.log(`Foo Test Web Component clicked!`);
            });
  });
</script>

问题:

在 HTML 实现中,阻止默认值不会阻止 link 工作。

问题:

如何让我的 Web 组件的最终用户防止默认,并阻止 link 工作?

我知道我可以在 fooClickHandler() 中添加 preventDefault()(见下文),但这对我来说似乎很奇怪。我想将控制权交给 Web 组件的最终用户。

@Event({
  eventName: 'foo-click',
  cancelable: true
}) fooClick: EventEmitter<MouseEvent>;
fooClickHandler(event: MouseEvent) {
  event.preventDefault();
  this.fooClick.emit();
}

一种方法是重载 addEventListener 函数并捕获函数 reference

(需要更多工作才能使其与嵌套元素一起使用,你会漂移)

或者使用自定义方法addClick(name,func) 这样用户仍然可以添加任何侦听器

<script>
  customElements.define(
    "my-element",
    class extends HTMLElement {
      connectedCallback() {
        this.clicked = (evt)=>{
          document.body.append("component handler")
        }
        this.onclick = (evt) => {
            this.clicked(evt);
        }
      }
      addEventListener(name, func) {
        this.clicked = func;
      }
    }
  );
  document.addEventListener('DOMContentLoaded', () => {
    document.querySelector('my-element')
      .addEventListener('click', event => {
        document.body.append(`user handler`);
      });
  });
</script>
<my-element>Hello Web Components World!</my-element>

您也可以使用旧的 onevent 处理程序:

<script>
  customElements.define(
    "my-element",
    class extends HTMLElement {
      connectedCallback() {
        this.onclick = (evt) => console.log("component handler")
      }
    }
  );
  document.addEventListener('DOMContentLoaded', () => {
    let el = document.querySelector('my-element');
    el.onclick = event => console.log(`user handler`, el.onclick);
  });
</script>
<my-element onclick="console.log('inline')">Hello Web Components World!</my-element>

有两个单独的事件:

  1. 用户发起的点击事件
  2. 您的 fooClick 自定义事件

在您的示例中,您在自定义事件上调用了 preventDefault(),但您需要在原始点击事件上调用它以防止 link 导航。

我知道有两种方法可以做到这一点:

1:跟踪您的自定义事件是否被取消

您可以使用 defaultPrevented 属性 检查用户是否在您的自定义事件中调用了 preventDefault()fooClick 事件处理程序可以保持不变。

fooClickHandler(clickEvent: MouseEvent) {
  const customEvent = this.fooClick.emit();
  if (customEvent.defaultPrevented) {
    clickEvent.preventDefault();
  }
}

查看 this online demo

2:传递点击事件

将点击事件传递给 fooClick 事件处理程序,以便用户可以取消它。

fooClickHandler(clickEvent: MouseEvent) {
  this.fooClick.emit({ originalEvent: clickEvent });
}

在处理程序中:

element.addEventListener('foo-click', event => {
  event.detail.originalEvent.preventDefault();
  console.log(`Foo Test Web Component clicked!`);
});