PageSourceSearch

https://www.brighthorizons.com/dist/js/focus-visible.js?v1.6

js brighthorizons.com collected 2026-10-01 08:42:10 UTC 11,032 bytes, 304 lines download raw bytes

vendor: 11,032 bytes, lines 1-304
1/**
2 * Applies the :focus-visible polyfill at the given scope.
3 * A scope in this case is either the top-level Document or a Shadow Root.
4 *
5 * @param {(Document|ShadowRoot)} scope
6 * @see https://github.com/WICG/focus-visible
7 */
8function applyFocusVisiblePolyfill(scope) {
9  var hadKeyboardEvent = true;
10  var hadFocusVisibleRecently = false;
11  var hadFocusVisibleRecentlyTimeout = null;
12
13  var inputTypesWhitelist = {
14    text: true,
15    search: true,
16    url: true,
17    tel: true,
18    email: true,
19    password: true,
20    number: true,
21    date: true,
22    month: true,
23    week: true,
24    time: true,
25    datetime: true,
26    'datetime-local': true
27  };
28
29  /**
30   * Helper function for legacy browsers and iframes which sometimes focus
31   * elements like document, body, and non-interactive SVG.
32   * @param {Element} el
33   */
34  function isValidFocusTarget(el) {
35    if (
36      el &&
37      el !== document &&
38      el.nodeName !== 'HTML' &&
39      el.nodeName !== 'BODY' &&
40      'classList' in el &&
41      'contains' in el.classList
42    ) {
43      return true;
44    }
45    return false;
46  }
47
48  /**
49   * Computes whether the given element should automatically trigger the
50   * `focus-visible` class being added, i.e. whether it should always match
51   * `:focus-visible` when focused.
52   * @param {Element} el
53   * @return {boolean}
54   */
55  function focusTriggersKeyboardModality(el) {
56    var type = el.type;
57    var tagName = el.tagName;
58
59    if (tagName === 'INPUT' && inputTypesWhitelist[type] && !el.readOnly) {
60      return true;
61    }
62
63    if (tagName === 'TEXTAREA' && !el.readOnly) {
64      return true;
65    }
66
67    if (el.isContentEditable) {
68      return true;
69    }
70
71    return false;
72  }
73
74  /**
75   * Add the `focus-visible` class to the given element if it was not added by
76   * the author.
77   * @param {Element} el
78   */
79  function addFocusVisibleClass(el) {
80    if (el.classList.contains('focus-visible')) {
81      return;
82    }
83    el.classList.add('focus-visible');
84    el.setAttribute('data-focus-visible-added', '');
85  }
86
87  /**
88   * Remove the `focus-visible` class from the given element if it was not
89   * originally added by the author.
90   * @param {Element} el
91   */
92  function removeFocusVisibleClass(el) {
93    if (!el.hasAttribute('data-focus-visible-added')) {
94      return;
95    }
96    el.classList.remove('focus-visible');
97    el.removeAttribute('data-focus-visible-added');
98  }
99
100  /**
101   * If the most recent user interaction was via the keyboard;
102   * and the key press did not include a meta, alt/option, or control key;
103   * then the modality is keyboard. Otherwise, the modality is not keyboard.
104   * Apply `focus-visible` to any current active element and keep track
105   * of our keyboard modality state with `hadKeyboardEvent`.
106   * @param {KeyboardEvent} e
107   */
108  function onKeyDown(e) {
109    if (e.metaKey || e.altKey || e.ctrlKey) {
110      return;
111    }
112
113    if (isValidFocusTarget(scope.activeElement)) {
114      addFocusVisibleClass(scope.activeElement);
115    }
116
117    hadKeyboardEvent = true;
118  }
119
120  /**
121   * If at any point a user clicks with a pointing device, ensure that we change
122   * the modality away from keyboard.
123   * This avoids the situation where a user presses a key on an already focused
124   * element, and then clicks on a different element, focusing it with a
125   * pointing device, while we still think we're in keyboard modality.
126   * @param {Event} e
127   */
128  function onPointerDown(e) {
129    hadKeyboardEvent = false;
130  }
131
132  /**
133   * On `focus`, add the `focus-visible` class to the target if:
134   * - the target received focus as a result of keyboard navigation, or
135   * - the event target is an element that will likely require interaction
136   *   via the keyboard (e.g. a text box)
137   * @param {Event} e
138   */
139  function onFocus(e) {
140    // Prevent IE from focusing the document or HTML element.
141    if (!isValidFocusTarget(e.target)) {
142      return;
143    }
144
145    if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
146      addFocusVisibleClass(e.target);
147    }
148  }
149
150  /**
151   * On `blur`, remove the `focus-visible` class from the target.
152   * @param {Event} e
153   */
154  function onBlur(e) {
155    if (!isValidFocusTarget(e.target)) {
156      return;
157    }
158
159    if (
160      e.target.classList.contains('focus-visible') ||
161      e.target.hasAttribute('data-focus-visible-added')
162    ) {
163      // To detect a tab/window switch, we look for a blur event followed
164      // rapidly by a visibility change.
165      // If we don't see a visibility change within 100ms, it's probably a
166      // regular focus change.
167      hadFocusVisibleRecently = true;
168      window.clearTimeout(hadFocusVisibleRecentlyTimeout);
169      hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {
170        hadFocusVisibleRecently = false;
171      }, 100);
172      removeFocusVisibleClass(e.target);
173    }
174  }
175
176  /**
177   * If the user changes tabs, keep track of whether or not the previously
178   * focused element had .focus-visible.
179   * @param {Event} e
180   */
181  function onVisibilityChange(e) {
182    if (document.visibilityState === 'hidden') {
183      // If the tab becomes active again, the browser will handle calling focus
184      // on the element (Safari actually calls it twice).
185      // If this tab change caused a blur on an element with focus-visible,
186      // re-apply the class when the user switches back to the tab.
187      if (hadFocusVisibleRecently) {
188        hadKeyboardEvent = true;
189      }
190      addInitialPointerMoveListeners();
191    }
192  }
193
194  /**
195   * Add a group of listeners to detect usage of any pointing devices.
196   * These listeners will be added when the polyfill first loads, and anytime
197   * the window is blurred, so that they are active when the window regains
198   * focus.
199   */
200  function addInitialPointerMoveListeners() {
201    document.addEventListener('mousemove', onInitialPointerMove);
202    document.addEventListener('mousedown', onInitialPointerMove);
203    document.addEventListener('mouseup', onInitialPointerMove);
204    document.addEventListener('pointermove', onInitialPointerMove);
205    document.addEventListener('pointerdown', onInitialPointerMove);
206    document.addEventListener('pointerup', onInitialPointerMove);
207    document.addEventListener('touchmove', onInitialPointerMove);
208    document.addEventListener('touchstart', onInitialPointerMove);
209    document.addEventListener('touchend', onInitialPointerMove);
210  }
211
212  function removeInitialPointerMoveListeners() {
213    document.removeEventListener('mousemove', onInitialPointerMove);
214    document.removeEventListener('mousedown', onInitialPointerMove);
215    document.removeEventListener('mouseup', onInitialPointerMove);
216    document.removeEventListener('pointermove', onInitialPointerMove);
217    document.removeEventListener('pointerdown', onInitialPointerMove);
218    document.removeEventListener('pointerup', onInitialPointerMove);
219    document.removeEventListener('touchmove', onInitialPointerMove);
220    document.removeEventListener('touchstart', onInitialPointerMove);
221    document.removeEventListener('touchend', onInitialPointerMove);
222  }
223
224  /**
225   * When the polfyill first loads, assume the user is in keyboard modality.
226   * If any event is received from a pointing device (e.g. mouse, pointer,
227   * touch), turn off keyboard modality.
228   * This accounts for situations where focus enters the page from the URL bar.
229   * @param {Event} e
230   */
231  function onInitialPointerMove(e) {
232    // Work around a Safari quirk that fires a mousemove on <html> whenever the
233    // window blurs, even if you're tabbing out of the page. ¯\_(ツ)_/¯
234    if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {
235      return;
236    }
237
238    hadKeyboardEvent = false;
239    removeInitialPointerMoveListeners();
240  }
241
242  // For some kinds of state, we are interested in changes at the global scope
243  // only. For example, global pointer input, global key presses and global
244  // visibility change should affect the state at every scope:
245  document.addEventListener('keydown', onKeyDown, true);
246  document.addEventListener('mousedown', onPointerDown, true);
247  document.addEventListener('pointerdown', onPointerDown, true);
248  document.addEventListener('touchstart', onPointerDown, true);
249  document.addEventListener('visibilitychange', onVisibilityChange, true);
250
251  addInitialPointerMoveListeners();
252
253  // For focus and blur, we specifically care about state changes in the local
254  // scope. This is because focus / blur events that originate from within a
255  // shadow root are not re-dispatched from the host element if it was already
256  // the active element in its own scope:
257  scope.addEventListener('focus', onFocus, true);
258  scope.addEventListener('blur', onBlur, true);
259
260  // We detect that a node is a ShadowRoot by ensuring that it is a
261  // DocumentFragment and also has a host property. This check covers native
262  // implementation and polyfill implementation transparently. If we only cared
263  // about the native implementation, we could just check if the scope was
264  // an instance of a ShadowRoot.
265  if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {
266    // Since a ShadowRoot is a special kind of DocumentFragment, it does not
267    // have a root element to add a class to. So, we add this attribute to the
268    // host element instead:
269    scope.host.setAttribute('data-js-focus-visible', '');
270  } else if (scope.nodeType === Node.DOCUMENT_NODE) {
271    document.documentElement.classList.add('js-focus-visible');
272    document.documentElement.setAttribute('data-js-focus-visible', '');
273  }
274}
275
276// It is important to wrap all references to global window and document in
277// these checks to support server-side rendering use cases
278// @see https://github.com/WICG/focus-visible/issues/199
279if (typeof window !== 'undefined' && typeof document !== 'undefined') {
280  // Make the polyfill helper globally available. This can be used as a signal
281  // to interested libraries that wish to coordinate with the polyfill for e.g.,
282  // applying the polyfill to a shadow root:
283  window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;
284
285  // Notify interested libraries of the polyfill's presence, in case the
286  // polyfill was loaded lazily:
287  var event;
288
289  try {
290    event = new CustomEvent('focus-visible-polyfill-ready');
291  } catch (error) {
292    // IE11 does not support using CustomEvent as a constructor directly:
293    event = document.createEvent('CustomEvent');
294    event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});
295  }
296
297  window.dispatchEvent(event);
298}
299
300if (typeof document !== 'undefined') {
301  // Apply the polyfill to the global document, so that no JavaScript
302  // coordination is required to use the polyfill in the top-level document:
303  applyFocusVisiblePolyfill(document);
304}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.