fix(docs-infra): preserve focus on copy (and prevent scrolling to bottom on IE11) (#38244)

The `CopierService` is used for copying text to the user's clipboard. It
is, for example, used in `CodeComponent` to copy example code snippets.
This is implemented by creating a temporary, hidden `<textarea>`
elements, setting its value to the text that needs to be copied,
executing the `copy` command and finally removing the element from the
DOM.

Previously, as a result of `CopierService`'s implementation, the focused
element would lose focus, while the temporary `<textarea>` element would
implicitly gain focus when selecting its contents. This had an even
worse side-effect on IE11, which seems to scroll to the bottom of the
containing element (here `<body>`) when the focused element is removed.

This commit fixes these issues by keeping track of the previously
focused element and restoring its focus after the copy operation.

NOTE: This fix is inspired by Angular CDK's [PendingCopy][1] class.

[1]: https://github.com/angular/components/blob/89b5fa89d1437c3054c5/src/cdk/clipboard/pending-copy.ts

Fixes #37796

PR Close #38244
This commit is contained in:
George Kalpakas 2020-07-29 17:38:11 +03:00 committed by Misko Hevery
parent 9ebd461a22
commit d96824acdb
1 changed files with 9 additions and 0 deletions

View File

@ -5,6 +5,9 @@
* - https://github.com/zenorocha/clipboard.js/
*
* Both released under MIT license - © Zeno Rocha
*
* It is also influenced by the Angular CDK `PendingCopy` class:
* https://github.com/angular/components/blob/master/src/cdk/clipboard/pending-copy.ts
*/
@ -18,6 +21,8 @@ export class CopierService {
* @return Whether the copy operation was successful.
*/
private copyTextArea(textArea: HTMLTextAreaElement): boolean {
const currentFocus = document.activeElement as HTMLOrSVGElement | null;
try {
textArea.select();
textArea.setSelectionRange(0, textArea.value.length);
@ -25,6 +30,10 @@ export class CopierService {
return document.execCommand('copy');
} catch {
return false;
} finally {
// Calling `.select()` on the `<textarea>` element may have also focused it.
// Change the focus back to the previously focused element.
currentFocus?.focus();
}
}