Click handler is not triggered when target element has been moved

The problem was discussed in issue #7113 on GitHub. It is caused by the fact that validation is performed when the input field loses focus, before the click on the button has a chance to complete and trigger the click event.

A possible workaround is to use a flag to keep the message hidden while clicking the button is under way, as shown in this stackblitz. In the code below, the isClicking flag is set when clicking starts on the mousedown event, and it is reset when the click event completes.

<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;
  }
}

You can improve that solution by replacing the setTimeout with a procedure to capture the mouse in the mousedown event handler, and resetting isClicking when the capture is lost. It would account for the cases where the user leaves the button without completing the click.


The issue seem to relate to DOM events triggering order

According to 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

In the given example the element moves the moment you blur the input -- because the validation happens instantly, reveals the error and repositions the button.

Therefore mouse is down when while over the button, but when its up -- the button is repositioned. So click wont be triggered on the button

There are several options to workaround that:

  • on mousedown delay the error reveal
  • hide the error until both mousedown and mouseup happened, if mousedown happened on the button
  • etc

Here's an example with mousedown event handling https://jsfiddle.net/gjbgqqpo/

Hope this helps :)