移动目标元素时不会触发点击处理程序
Click handler is not triggered when target element has been moved
考虑以下演示 https://stackblitz.com/edit/angular-pur1dt
我有带有同步验证器的反应式表单控件,当它失效时,错误消息显示在字段下方。
控件失去焦点时触发验证。在控件下方有一个带有点击处理程序的按钮。问题是,当我单击按钮时,控件失去焦点,进行验证,显示错误消息并将按钮向下移动。据推测,这会阻止点击处理程序的执行。有什么关于为什么会发生这种情况以及如何解决问题的建议吗?
我已经用评论更新了演示。注意:只有输入下方的按钮会重现该问题。第一次点击标题不会更新
好的,我想我明白了。您不希望在单击下方按钮时发生验证。
触发验证的原因是表单输入中的 autofocus
。 Angular "By default, the value and validity of a control updates whenever the value changes." See FormControl。控件正在将 name.untouched
从 false
更改为 true
。
如果您向模板添加一些额外的调试绑定,您可以看到这种情况发生。
例如
<hello name="{{ name }}"></hello>
<form [formGroup]="form">
<input formControlName="name" autofocus>
<button (click)="onClick($event)">click OK</button>
</form>
<pre>{{form.controls.name.valid}}</pre>
<pre>{{form.controls.name.untouched}}</pre>
<p [hidden]="form.controls.name.valid || form.controls.name.untouched ">
Invalid :)
</p>
<button (click)="onClick2($event)">click NOT ok</button>
如果你想在用户点击下方按钮时隐藏错误信息,你应该删除autofocus
或者在调用onClick2()
时重置表单的验证。或者,您可以在表单出错时只显示错误消息。 Angular 说 A control is untouched if the user has not yet triggered a blur event on it.
单击下方按钮会导致模糊事件。
为什么不直接将错误消息上的模板绑定更改为
<p [hidden]="!form.controls.name.valid || form.controls.name.untouched">
Invalid :)
</p>
此问题已在 GitHub 的 issue #7113 中讨论过。这是因为在输入字段失去焦点时执行验证,在按钮的单击有机会完成并触发 click
事件之前。
一种可能的解决方法是在单击按钮时使用标志来隐藏消息,如 this stackblitz 所示。在下面的代码中,isClicking
标志在 mousedown
事件开始时设置,并在 click
事件完成时重置。
<p #errorMsg [hidden]="(errorMsg.hidden && isClicking) || form.controls.name.valid || form.controls.name.untouched ">
Invalid :)
</p>
<button (mousedown)="onMouseDown($event)" (click)="onClick2($event)">click NOT ok</button>
export class AppComponent {
isClicking = false;
...
onMouseDown(event) {
this.isClicking = true;
setTimeout(() => {
// The click action began but was not completed after two seconds
this.isClicking = false;
}, 2000);
}
onClick2(event) {
console.log(event);
this.name = "NOT";
this.isClicking = false;
}
}
您可以改进该解决方案,方法是将 setTimeout
替换为在 mousedown
事件处理程序中捕获鼠标的过程,并在捕获丢失时重置 isClicking
。它会考虑用户在未完成点击的情况下离开按钮的情况。
问题似乎与 DOM 事件触发顺序有关
根据 MDN:
The click event is fired when a pointing device button (usually a
mouse's primary button) is pressed and released on a single element.
https://developer.mozilla.org/en-US/docs/Web/Events/click
在给定的示例中,元素在您 blur
输入的那一刻移动 - 因为验证立即发生,显示错误并重新定位按钮。
因此,当 在按钮上方时,鼠标 处于按下状态,但是当 处于上升状态时 -- 按钮被重新定位。所以click
不会在按钮上触发
有几种解决方法:
- on
mousedown
延迟错误显示
- 隐藏错误,直到
mousedown
和 mouseup
都发生,如果 mousedown
发生在按钮上
- 等等
这是一个 mousedown
事件处理的例子
https://jsfiddle.net/gjbgqqpo/
希望这对您有所帮助:)
一个简单的解决方案是将 (mousedown)="false"
添加到您的关闭按钮。这样就不会发生模糊事件,因为你的按钮上的焦点事件将被你的 mousedown 处理程序取消。反过来,这不会将控件标记为已触摸,您的错误也不会显示。
<button type="button" (click)="dismiss()" (mousedown)="false">Cancel</button>
现在对于您的提交按钮,您不想取消模糊事件,但您可能想要 运行 mousedown 上的某种验证逻辑。这样想,如果按钮移动了,那就意味着表单有错误。当表单出现错误时你想做什么?就我而言,我真正想做的就是将所有控件标记为已触摸,以便显示所有验证错误。
<button type="submit" (mousedown)="form.markAllAsTouched()">Create</button>
考虑以下演示 https://stackblitz.com/edit/angular-pur1dt
我有带有同步验证器的反应式表单控件,当它失效时,错误消息显示在字段下方。
控件失去焦点时触发验证。在控件下方有一个带有点击处理程序的按钮。问题是,当我单击按钮时,控件失去焦点,进行验证,显示错误消息并将按钮向下移动。据推测,这会阻止点击处理程序的执行。有什么关于为什么会发生这种情况以及如何解决问题的建议吗?
我已经用评论更新了演示。注意:只有输入下方的按钮会重现该问题。第一次点击标题不会更新
好的,我想我明白了。您不希望在单击下方按钮时发生验证。
触发验证的原因是表单输入中的 autofocus
。 Angular "By default, the value and validity of a control updates whenever the value changes." See FormControl。控件正在将 name.untouched
从 false
更改为 true
。
如果您向模板添加一些额外的调试绑定,您可以看到这种情况发生。 例如
<hello name="{{ name }}"></hello>
<form [formGroup]="form">
<input formControlName="name" autofocus>
<button (click)="onClick($event)">click OK</button>
</form>
<pre>{{form.controls.name.valid}}</pre>
<pre>{{form.controls.name.untouched}}</pre>
<p [hidden]="form.controls.name.valid || form.controls.name.untouched ">
Invalid :)
</p>
<button (click)="onClick2($event)">click NOT ok</button>
如果你想在用户点击下方按钮时隐藏错误信息,你应该删除autofocus
或者在调用onClick2()
时重置表单的验证。或者,您可以在表单出错时只显示错误消息。 Angular 说 A control is untouched if the user has not yet triggered a blur event on it.
单击下方按钮会导致模糊事件。
为什么不直接将错误消息上的模板绑定更改为
<p [hidden]="!form.controls.name.valid || form.controls.name.untouched">
Invalid :)
</p>
此问题已在 GitHub 的 issue #7113 中讨论过。这是因为在输入字段失去焦点时执行验证,在按钮的单击有机会完成并触发 click
事件之前。
一种可能的解决方法是在单击按钮时使用标志来隐藏消息,如 this stackblitz 所示。在下面的代码中,isClicking
标志在 mousedown
事件开始时设置,并在 click
事件完成时重置。
<p #errorMsg [hidden]="(errorMsg.hidden && isClicking) || form.controls.name.valid || form.controls.name.untouched ">
Invalid :)
</p>
<button (mousedown)="onMouseDown($event)" (click)="onClick2($event)">click NOT ok</button>
export class AppComponent {
isClicking = false;
...
onMouseDown(event) {
this.isClicking = true;
setTimeout(() => {
// The click action began but was not completed after two seconds
this.isClicking = false;
}, 2000);
}
onClick2(event) {
console.log(event);
this.name = "NOT";
this.isClicking = false;
}
}
您可以改进该解决方案,方法是将 setTimeout
替换为在 mousedown
事件处理程序中捕获鼠标的过程,并在捕获丢失时重置 isClicking
。它会考虑用户在未完成点击的情况下离开按钮的情况。
问题似乎与 DOM 事件触发顺序有关
根据 MDN:
The click event is fired when a pointing device button (usually a mouse's primary button) is pressed and released on a single element.
https://developer.mozilla.org/en-US/docs/Web/Events/click
在给定的示例中,元素在您 blur
输入的那一刻移动 - 因为验证立即发生,显示错误并重新定位按钮。
因此,当 在按钮上方时,鼠标 处于按下状态,但是当 处于上升状态时 -- 按钮被重新定位。所以click
不会在按钮上触发
有几种解决方法:
- on
mousedown
延迟错误显示 - 隐藏错误,直到
mousedown
和mouseup
都发生,如果mousedown
发生在按钮上 - 等等
这是一个 mousedown
事件处理的例子
https://jsfiddle.net/gjbgqqpo/
希望这对您有所帮助:)
一个简单的解决方案是将 (mousedown)="false"
添加到您的关闭按钮。这样就不会发生模糊事件,因为你的按钮上的焦点事件将被你的 mousedown 处理程序取消。反过来,这不会将控件标记为已触摸,您的错误也不会显示。
<button type="button" (click)="dismiss()" (mousedown)="false">Cancel</button>
现在对于您的提交按钮,您不想取消模糊事件,但您可能想要 运行 mousedown 上的某种验证逻辑。这样想,如果按钮移动了,那就意味着表单有错误。当表单出现错误时你想做什么?就我而言,我真正想做的就是将所有控件标记为已触摸,以便显示所有验证错误。
<button type="submit" (mousedown)="form.markAllAsTouched()">Create</button>