Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance FocusMonitor to communicate window deactivation vs other forms of focus loss #15744

Open
pcafstockf opened this issue Apr 6, 2019 · 2 comments
Labels
area: cdk/a11y feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions

Comments

@pcafstockf
Copy link

pcafstockf commented Apr 6, 2019

Please describe the feature you would like to request.

Please extend the FocusMonitor to better align with document.activeElement, by treating window deactivation as a slightly different case than a loss of focus resulting from the user actually clicking on a different element of the page. Also a way to detect window re-activation as opposed to user selection of an element.

What is the use-case or motivation for this proposal?

When a browser window loses focus, document.activeElement is not changed. From the DOM's point of view, there is still an "active" element, but the FocusMonitor treats this the same as if the user had simply clicked somewhere else on the page. There are cases where we don't want to blur the state of the page just because it is no longer the active window, and cases where we don't want to go through a re-focus process just because the window became active again.

Is there anything else we should know?

This change could easily be implemented with minimal breakage by using undefined to mean window deactivated (as both undefined and null are falsey). 'window' could then be added as a FocusOrigin value to signal window activation.

The implementation below has been working well in my own project, but it seems useful enough to merge into the base FocusMonitor.

`
export type FocusOriginEx = FocusOrigin | 'window' | undefined;

@Injectable()
export class FocusMonitorEx extends FocusMonitor {

constructor(ngZone: NgZone, platform: Platform) {
	super(ngZone, platform);
}
private inactiveElement: Element;

/**
 * @see #monitor
 * The difference for this Ex (aka Extended) routine is to be able to also detect window de/re-activation.
 * This activity might affect how the item displays itself, but it does not mean the document's activeElement was changed.
 * This is important for instance with a matInput that has a mat-autocomplete currently open.
 * If focus was shifted elsewhere in the document, null will be emitted (as before).
 * However, if the window was deactivated but document.activeElement remains unchanged, this will emit undefined (closest thing to null).
 * When the window is re-activated, but document.activeElement remains *unchanged*, 'window' will be emitted (instead of the original source (e.g. mouse, keyboard, etc.)
 */
monitorEx(element: HTMLElement | ElementRef<HTMLElement>, checkChildren?: boolean): Observable<FocusOriginEx> {
	return super.monitor(<any>element, checkChildren).pipe(map((origin: FocusOrigin) => {
		if (origin === null) {
			let elem = element;
			if (elem instanceof ElementRef)
				elem = elem.nativeElement;
			let matchesMonitor = function (e: Element): boolean {
				if (e === null)
					return false;
				if (e === elem) // This is guaranteed by the standard to be equivalent to Node.isSame (but Node.isSame is not supported everywhere).
					return true;
				if (checkChildren && e.parentElement)
					return matchesMonitor(e.parentElement);
				return false;
			};
			if (matchesMonitor(document.activeElement)) {
				this.inactiveElement = document.activeElement;
				return undefined;
			}
		} else if (this.inactiveElement) {
			delete this.inactiveElement;
			return 'window';
		}
		return origin;
	}));
}

}
`

@andrewseguin andrewseguin added the feature This issue represents a new feature or feature request rather than a bug or bug fix label Apr 12, 2019
@mmalerba mmalerba added the needs triage This issue needs to be triaged by the team label May 20, 2020
@crisbeto crisbeto added area: cdk/a11y P4 A relatively minor issue that is not relevant to core functions and removed needs triage This issue needs to be triaged by the team labels May 28, 2020
@angular-robot
Copy link
Contributor

angular-robot bot commented Feb 21, 2022

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

@angular-robot
Copy link
Contributor

angular-robot bot commented Mar 13, 2022

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: cdk/a11y feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions
Projects
None yet
Development

No branches or pull requests

4 participants