UNPKG

3.01 kBPlain TextView Raw
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13// This is a polyfill for element.focus({preventScroll: true});
14// Currently necessary for Safari and old Edge:
15// https://caniuse.com/#feat=mdn-api_htmlelement_focus_preventscroll_option
16// See https://bugs.webkit.org/show_bug.cgi?id=178583
17//
18
19// Original licensing for the following methods can be found in the
20// NOTICE file in the root directory of this source tree.
21// See https://github.com/calvellido/focus-options-polyfill
22
23interface ScrollableElement {
24 element: HTMLElement,
25 scrollTop: number,
26 scrollLeft: number
27}
28
29export function focusWithoutScrolling(element: HTMLElement) {
30 if (supportsPreventScroll()) {
31 element.focus({preventScroll: true});
32 } else {
33 let scrollableElements = getScrollableElements(element);
34 element.focus();
35 restoreScrollPosition(scrollableElements);
36 }
37}
38
39let supportsPreventScrollCached: boolean = null;
40function supportsPreventScroll() {
41 if (supportsPreventScrollCached == null) {
42 supportsPreventScrollCached = false;
43 try {
44 var focusElem = document.createElement('div');
45 focusElem.focus({
46 get preventScroll() {
47 supportsPreventScrollCached = true;
48 return true;
49 }
50 });
51 } catch (e) {
52 // Ignore
53 }
54 }
55
56 return supportsPreventScrollCached;
57}
58
59function getScrollableElements(element: HTMLElement): ScrollableElement[] {
60 var parent = element.parentNode;
61 var scrollableElements: ScrollableElement[] = [];
62 var rootScrollingElement = document.scrollingElement || document.documentElement;
63
64 while (parent instanceof HTMLElement && parent !== rootScrollingElement) {
65 if (
66 parent.offsetHeight < parent.scrollHeight ||
67 parent.offsetWidth < parent.scrollWidth
68 ) {
69 scrollableElements.push({
70 element: parent,
71 scrollTop: parent.scrollTop,
72 scrollLeft: parent.scrollLeft
73 });
74 }
75 parent = parent.parentNode;
76 }
77
78 if (rootScrollingElement instanceof HTMLElement) {
79 scrollableElements.push({
80 element: rootScrollingElement,
81 scrollTop: rootScrollingElement.scrollTop,
82 scrollLeft: rootScrollingElement.scrollLeft
83 });
84 }
85
86 return scrollableElements;
87}
88
89function restoreScrollPosition(scrollableElements: ScrollableElement[]) {
90 for (let {element, scrollTop, scrollLeft} of scrollableElements) {
91 element.scrollTop = scrollTop;
92 element.scrollLeft = scrollLeft;
93 }
94}