如何防止在 StencilJs 中点击禁用按钮
How to prevent onClick on disabled button in StencilJs
我创建了自己的自定义按钮,如下所示
@Component({
tag: "idv-button",
styleUrl: "idv-button.scss",
shadow: true,
})
export class IdvButton {
@Prop({ reflect: true }) text: string;
@Prop({ reflect: true }) disabled: boolean = false;
render(): any {
return (
<button disabled={this.disabled} onClick={this.onClick}>
<span class="btn-text">{this.text}</span>
</button>
);
}
private onClick(event: Event): void {
if (this.disabled) {
console.log("prevent default", event); // it doesn't come here at all
event.preventDefault();
}
}
}
并在 stenciljs 的另一个组件中像这样使用它
<idv-button
text="my button"
disabled={true}
onClick={this.startClick.bind(this)}>
</idv-button>
问题是虽然按钮是 disabled
并且即使我试图阻止默认,但 onClick 仍然发生并且 this.startClick
被调用
我该如何解决这个问题?
我认为 on...
属性如何与自定义元素一起使用存在一些混淆。在 Stencil 组件上,如果属性以 on
开头,那么它将成为与属性其余部分同名的事件处理程序(大小写会自动转换)。例如如果您的组件发出一个名为 myEvent
的事件(使用 Stencil 的 @Event
装饰器和 EventEmitter
类型),那么您可以使用 onMyEvent
属性为该事件添加一个监听器到您的组件.文档 here.
中的示例对此进行了解释
在您的情况下,您要向组件添加一个 onClick
属性,该属性处理该元素的 click
事件,这是一个原生 DOM 事件,不不需要设置,我。 e.任何 HTML 或自定义元素在被单击时都会触发 click
事件(不仅仅是按钮)。
所以即使你的组件就像
@Component({ tag: "idv-button" })
export class IdvButton {
render() {
return <p>Hello, World!</p>;
}
}
而且你用得像
<idv-button onClick={console.log} />
您每次单击文本时仍会收到 click
事件。
我认为您想要实现的是将 onClick
处理程序传递给底层按钮。最简单的方法是将处理函数作为 prop 传递。
import { Component, h, Host, Prop } from '@stencil/core';
@Component({ tag: "idv-button", shadow: true })
export class IdvButton {
@Prop() disabled?: boolean;
@Prop() clickHandler: (e: MouseEvent) => void;
render() {
return (
<button disabled={this.disabled} onClick={this.clickHandler.bind(this)}>
<slot />
</button>
);
}
}
<idv-button clickHandler={console.log}>
My Button
</idv-button>
不确定您是否真的需要绑定 this
,并且还更改了它以将内容作为插槽传递,但请随时放弃该建议。
顺便说一句,调用道具 onClick
非常诱人,但这会与原生 click
事件的 on...
事件处理发生冲突,因此 Stencil 会在编译时警告您-时间。
另一种解决方案是在设置 disabled
属性时将 pointer-events: none
添加到主机。这就是 Ionic 的 ion-button
所做的(参见 button.tsx#L220 and button.scss#L70-L74)。
在你的情况下它会是这样的
import { Component, Host, h, Prop } from '@stencil/core';
@Component({ tag: "idv-button", shadow: true })
export class IdvButton {
@Prop() disabled?: boolean;
render() {
return (
<Host style={{ pointerEvents: this.disabled ? 'none' : undefined }}>
<button disabled={this.disabled}>
<span class="btn-text">
<slot />
</span>
</button>
</Host>
);
}
}
(onClick
属性会自动作用于您的组件,因为它是默认事件)
问题分析
禁用不是禁用
一个问题是,<idv-button disabled>
的计算结果为 this.disabled===""
(空字符串),它被解释为 false
而不是 true
。
Chrome 捕获 onClick
另一个问题,在解决了上述问题后,它在 Firefox 中可以正常工作,但在 Chrome 中却不行。 Chrome 不给你机会自己处理 onClick
,Chrome 处理它。
点击保留
保留的onClick
的处理方式完全不同。所有 on…
属性都可以在 HTML 环境中采用字符串中的函数。但是如果你定义了自己的函数,没有以on…
-前缀命名,你不能直接在HTML中添加函数。所以,你必须用不同的名字定义你自己的函数,因为 onClick
的特殊行为,所以你需要在普通的 HTML 中特殊处理它。在 JSX 中,一切都符合预期。
提示:名称onClick
是保留的,所以是隐式的,不需要添加为@Prop
。
普通函数属性HTML
如果您需要在普通 HTML 环境(不是 JSX)中添加函数,则需要单独添加函数。另见:Pass functions to stencil component.
<idv-button>...</idv-button>
<script>
document.currentScript.previousElementSibling.clickHandler =
function() { ... }
</script>
(将函数clickHandler
添加到脚本上方的元素)
简单的解决方案
顺便说一句:我更喜欢将文本放在标签内,所以我使用 <slot/>
组件定义
以下解决了所有这些问题并完美运行:
Component({tag: 'idv-button'})
export class IdvButton {
@Prop() disabled?: any
@Prop() clickHandler: (e: MouseEvent) => void;
render() {
return (
<button
disabled={this.disabled || this.disabled === ""}
onClick={this.clickHandler}
>
<slot />
</button>
)
}
}
在非 JSX Plain 中使用-HTML
要在普通 HTML 中使用它,您需要在单独的 JavaScript 中添加函数(这里我将相同的函数添加到所有 idv-button
-元素):
<html>
<head>
<meta charset="utf-8" />
<script type="module" src="/build/my-webcomponents.esm.js"></script>
<script nomodule src="/build/my-webcomponents.js"></script>
</head>
<body>
<idv-button>Enabled</idv-button>
<idv-button disabled>Disabled</idv-button>
<script>
var allButtonInstances = document.querySelectorAll('idv-button')
allButtonInstances.forEach(e => e.clickHandler = function() {
alert('here');
})
</script>
</body>
</html>
我来晚了,但是用 {...(this.disabled || null)}
代替 disabled={this.disabled}
怎么样?
我创建了自己的自定义按钮,如下所示
@Component({
tag: "idv-button",
styleUrl: "idv-button.scss",
shadow: true,
})
export class IdvButton {
@Prop({ reflect: true }) text: string;
@Prop({ reflect: true }) disabled: boolean = false;
render(): any {
return (
<button disabled={this.disabled} onClick={this.onClick}>
<span class="btn-text">{this.text}</span>
</button>
);
}
private onClick(event: Event): void {
if (this.disabled) {
console.log("prevent default", event); // it doesn't come here at all
event.preventDefault();
}
}
}
并在 stenciljs 的另一个组件中像这样使用它
<idv-button
text="my button"
disabled={true}
onClick={this.startClick.bind(this)}>
</idv-button>
问题是虽然按钮是 disabled
并且即使我试图阻止默认,但 onClick 仍然发生并且 this.startClick
被调用
我该如何解决这个问题?
我认为 on...
属性如何与自定义元素一起使用存在一些混淆。在 Stencil 组件上,如果属性以 on
开头,那么它将成为与属性其余部分同名的事件处理程序(大小写会自动转换)。例如如果您的组件发出一个名为 myEvent
的事件(使用 Stencil 的 @Event
装饰器和 EventEmitter
类型),那么您可以使用 onMyEvent
属性为该事件添加一个监听器到您的组件.文档 here.
在您的情况下,您要向组件添加一个 onClick
属性,该属性处理该元素的 click
事件,这是一个原生 DOM 事件,不不需要设置,我。 e.任何 HTML 或自定义元素在被单击时都会触发 click
事件(不仅仅是按钮)。
所以即使你的组件就像
@Component({ tag: "idv-button" })
export class IdvButton {
render() {
return <p>Hello, World!</p>;
}
}
而且你用得像
<idv-button onClick={console.log} />
您每次单击文本时仍会收到 click
事件。
我认为您想要实现的是将 onClick
处理程序传递给底层按钮。最简单的方法是将处理函数作为 prop 传递。
import { Component, h, Host, Prop } from '@stencil/core';
@Component({ tag: "idv-button", shadow: true })
export class IdvButton {
@Prop() disabled?: boolean;
@Prop() clickHandler: (e: MouseEvent) => void;
render() {
return (
<button disabled={this.disabled} onClick={this.clickHandler.bind(this)}>
<slot />
</button>
);
}
}
<idv-button clickHandler={console.log}>
My Button
</idv-button>
不确定您是否真的需要绑定 this
,并且还更改了它以将内容作为插槽传递,但请随时放弃该建议。
顺便说一句,调用道具 onClick
非常诱人,但这会与原生 click
事件的 on...
事件处理发生冲突,因此 Stencil 会在编译时警告您-时间。
另一种解决方案是在设置 disabled
属性时将 pointer-events: none
添加到主机。这就是 Ionic 的 ion-button
所做的(参见 button.tsx#L220 and button.scss#L70-L74)。
在你的情况下它会是这样的
import { Component, Host, h, Prop } from '@stencil/core';
@Component({ tag: "idv-button", shadow: true })
export class IdvButton {
@Prop() disabled?: boolean;
render() {
return (
<Host style={{ pointerEvents: this.disabled ? 'none' : undefined }}>
<button disabled={this.disabled}>
<span class="btn-text">
<slot />
</span>
</button>
</Host>
);
}
}
(onClick
属性会自动作用于您的组件,因为它是默认事件)
问题分析
禁用不是禁用
一个问题是,<idv-button disabled>
的计算结果为 this.disabled===""
(空字符串),它被解释为 false
而不是 true
。
Chrome 捕获 onClick
另一个问题,在解决了上述问题后,它在 Firefox 中可以正常工作,但在 Chrome 中却不行。 Chrome 不给你机会自己处理 onClick
,Chrome 处理它。
点击保留
保留的onClick
的处理方式完全不同。所有 on…
属性都可以在 HTML 环境中采用字符串中的函数。但是如果你定义了自己的函数,没有以on…
-前缀命名,你不能直接在HTML中添加函数。所以,你必须用不同的名字定义你自己的函数,因为 onClick
的特殊行为,所以你需要在普通的 HTML 中特殊处理它。在 JSX 中,一切都符合预期。
提示:名称onClick
是保留的,所以是隐式的,不需要添加为@Prop
。
普通函数属性HTML
如果您需要在普通 HTML 环境(不是 JSX)中添加函数,则需要单独添加函数。另见:Pass functions to stencil component.
<idv-button>...</idv-button>
<script>
document.currentScript.previousElementSibling.clickHandler =
function() { ... }
</script>
(将函数clickHandler
添加到脚本上方的元素)
简单的解决方案
顺便说一句:我更喜欢将文本放在标签内,所以我使用 <slot/>
组件定义
以下解决了所有这些问题并完美运行:
Component({tag: 'idv-button'})
export class IdvButton {
@Prop() disabled?: any
@Prop() clickHandler: (e: MouseEvent) => void;
render() {
return (
<button
disabled={this.disabled || this.disabled === ""}
onClick={this.clickHandler}
>
<slot />
</button>
)
}
}
在非 JSX Plain 中使用-HTML
要在普通 HTML 中使用它,您需要在单独的 JavaScript 中添加函数(这里我将相同的函数添加到所有 idv-button
-元素):
<html>
<head>
<meta charset="utf-8" />
<script type="module" src="/build/my-webcomponents.esm.js"></script>
<script nomodule src="/build/my-webcomponents.js"></script>
</head>
<body>
<idv-button>Enabled</idv-button>
<idv-button disabled>Disabled</idv-button>
<script>
var allButtonInstances = document.querySelectorAll('idv-button')
allButtonInstances.forEach(e => e.clickHandler = function() {
alert('here');
})
</script>
</body>
</html>
我来晚了,但是用 {...(this.disabled || null)}
代替 disabled={this.disabled}
怎么样?