Reputation: 4072
Example Stencil.js web component:
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>
Problem:
In the HTML implementation, the prevent default does not stop the link from working.
Question:
How can I allow the end-user of my web component to prevent default, and stop the link from working?
I know that I can add preventDefault()
in the fooClickHandler()
(see below), but that seems odd to me. I'd like to give the control to the end user of the web component.
@Event({
eventName: 'foo-click',
cancelable: true
}) fooClick: EventEmitter<MouseEvent>;
fooClickHandler(event: MouseEvent) {
event.preventDefault();
this.fooClick.emit();
}
Upvotes: 0
Views: 921
Reputation: 8849
There are two separate events:
fooClick
custom eventIn your example you call preventDefault()
on the custom event but you need to call it on the original click event to prevent the link from navigating.
I know of two ways to achieve this:
1: Track whether your custom event is canceled
You can check whether the user called preventDefault()
on your custom event using the defaultPrevented
property. The fooClick
event handler can stay the same.
fooClickHandler(clickEvent: MouseEvent) {
const customEvent = this.fooClick.emit();
if (customEvent.defaultPrevented) {
clickEvent.preventDefault();
}
}
Check out this online demo.
2: Pass the click event
Pass the click event to the fooClick
event handler so the user can cancel it.
fooClickHandler(clickEvent: MouseEvent) {
this.fooClick.emit({ originalEvent: clickEvent });
}
And in the handler:
element.addEventListener('foo-click', event => {
event.detail.originalEvent.preventDefault();
console.log(`Foo Test Web Component clicked!`);
});
Upvotes: 1
Reputation: 21173
One way would be to overload the addEventListener
function and capture the function reference
(needs some more work to make it work with nested elements, you get drift)
Or use a custom method addClick(name,func)
so the user can still add any listener
<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>
You could also use good old onevent
handlers:
<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>
Upvotes: 1