(focusout) is triggered a full 150 milliseconds before (click) and that is anti-pattern
See original GitHub issueI’m submitting a…
[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Performance issue
[X] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:
Current behavior
say you have a focused div with a focusout directive and a click in order to counteract the focusout :
<div
tabindex="1"
*ngIf="importCsvOpen"
[@dropDownAnimation]
(click)="clickInside()"
[ngClass]="dropzoneHovered ? 'dragged' : ''"
class="dropzone-container"
(dragover)="dropzoneHovered = true"
(dragleave)="dropzoneHovered = false"
(focusout)="focusOut()"
myAutofocus>
focusout is inherently superior to @HostListener('document:click', ['$event']) clickedOutside(e){
because it means we no longer need to rely on beacons (classes, ids, jquery select, ect…) which are unreliable at best and have very little code robustness (someone else picks up the project, forgets to include the beacon class in his modifications then scratches his head at why his code doesn’t work)
focusout on the other hand is a method call, that is contained and in a controlled environment has a single point of origin and can be traced.
all that’s left is the ability to have the same cleanliness on the !focusout.
what if I click on the focused element?
not only does this trigger focusout but focusout is prioritary by an obscene order of magnitude.
I thought I could simply allow a bit of dirty code to fix the fact that Angular put (focusout)
before (click)
in runtime myself :
clickIn = false;
clickInside(){
this.clickIn = true;
setTimeout(()=> this.clickIn = false, 9);
}
focusOut(){
if(!this.clickIn){
this.importCsvOpen = false;
this.dropzoneHovered = false;
}
}
but that would turn out not to be enough because focusOut was being called before clickIn, so I had to make things worse :
clickIn = false;
clickInside(){
this.clickIn = true;
setTimeout(()=> this.clickIn = false, 190);
}
focusOut(){
setTimeout(()=> {
if(!this.clickIn){
this.importCsvOpen = false;
this.dropzoneHovered = false;
}
}, 150);
}
As you can see my attempt is to disallow the hiding of my dropzone if it was the dropzone itself that was clicked and I would know this as a certainty without having to use “beacons” if the origin of the (click)
method is the div that I am talking about and of course that, contrary to the “beacon” method, is the only possibility if we’re using the (click)
method on that div.
the problem then comes from trying to get the click method to be called before the focusout method and I couldn’t come up with another way to do this other than timeouts.
Were I loose all hope is that the delay required to actually succeed in this is utterly obscene.
I understand that there’s a lot going on with that div but how is Angular triggering (click)
150 milliseconds after the (focusout)
. there must be some sort of performance issue here too.
Thinking long and hard about it I have come to the conclusion that there could not be a use-case for requirering (focusout)
being called before (click)
that wouldn’t be immediately remedied by using (click)
like I did, except with the inverse of the boolean logic demonstrated above.
Expected behavior
that (click)
always trigger before (focusout)
.
as an added bonus focusout shouldn’t be triggered by clicking on the focused div that itself is the issuer of the focusout call. Which does not make sense to me. (but if it stays that way I can work around that, given (click)
is brought up in the priority chain)
Minimal reproduction of the problem with instructions
https://stackblitz.com/edit/angular-gitter-u7erfg?file=app%2Fapp.component.ts
What is the motivation / use case for changing the behavior?
the other way around would make much more sense and be much more useful to people
Environment
Angular version: 6.0.4
Browser:
- [x ] Chrome (desktop) version XX
- [x ] Chrome (Android) version XX
- [x ] Chrome (iOS) version XX
- [x ] Firefox version XX
- [x ] Safari (desktop) version XX
- [x ] Safari (iOS) version XX
- [x ] IE version XX
- [x ] Edge version XX
For Tooling issues:
- Node version: v8.9.4
- Platform: Windows
Issue Analytics
- State:
- Created 5 years ago
- Comments:6 (3 by maintainers)
Top GitHub Comments
what I’m saying is they should. there’s every motive to do so. they could gain additional functionality and code cleanliness as well as runtime reliability as well as potentially utilize those to unlock new capabilities that the other frameworks won’t have. And from where I’m standing this overall could mean optimization and performance also.
As @sarunint says, Angular is doing nothing special here, and we are not doing anything like
Angularfocusout(args){ js.focusout(args)}
.focusout
is a DOM Event, (not a method) and so internally,<input (focusout)="doStuff()">
literally gets compiled toinput.addEventListener('focusout', e => component.doStuff())
. The browser triggers it, Angular does not!In general, we smooth over the rough edges where possible, without changing the semantics of how the DOM works. This would appear to dramatically change the native behavior (however irritating it is), and we’re not likely to do that.