PageSourceSearch

https://www.elle.cz/js/modules/headerScrollReveal.js

js elle.cz collected 2026-10-02 07:55:22 UTC 3,964 bytes, 121 lines download raw bytes

1import {createRafScheduler} from '../core/rafScheduler.js';
2import {initBrandingAdOffset} from './brandingAdOffset.js';
3
4const DIRECTION_THRESHOLD = 8;
5const OPEN_MENU_SELECTOR = '.mobile-nav.is-open, [data-header-submenu].is-open, [data-mega-menu-dropdown].is-open';
6
7const getScrollPosition = () => {
8  const maxScroll = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
9  return Math.min(maxScroll, Math.max(0, window.scrollY));
10};
11
12export const initHeaderScrollReveal = () => {
13  const header = document.querySelector('[data-js-module~="header-scroll-reveal"]');
14  const container = header?.closest('.site-header-container');
15  if (!(header instanceof HTMLElement) || !(container instanceof HTMLElement)
16    || container.classList.contains('site-header-container--scroll-reveal')) {
17    return;
18  }
19
20  let previousY = getScrollPosition();
21  let headerHeight = header.offsetHeight;
22  let direction = 0;
23  let travel = 0;
24  let isHidden = false;
25  const syncHeaderOffset = initBrandingAdOffset();
26
27  const isInteractionActive = () => header.contains(document.activeElement)
28    || header.querySelector(OPEN_MENU_SELECTOR) !== null
29    || document.body.classList.contains('search-open');
30
31  const setHidden = (hidden) => {
32    if (hidden === isHidden) {
33      return;
34    }
35
36    isHidden = hidden;
37    container.classList.toggle('is-scroll-hidden', hidden);
38    syncHeaderOffset?.();
39  };
40
41  const resetDirection = () => {
42    previousY = getScrollPosition();
43    direction = 0;
44    travel = 0;
45  };
46
47  const updateFromScroll = () => {
48    const currentY = getScrollPosition();
49    const delta = currentY - previousY;
50    previousY = currentY;
51
52    if (currentY <= headerHeight || isInteractionActive()) {
53      resetDirection();
54      setHidden(false);
55      return;
56    }
57
58    if (delta === 0) {
59      return;
60    }
61
62    const nextDirection = Math.sign(delta);
63    travel = nextDirection === direction ? travel + Math.abs(delta) : Math.abs(delta);
64    direction = nextDirection;
65    if (travel >= DIRECTION_THRESHOLD) {
66      setHidden(direction > 0);
67    }
68  };
69
70  const scrollScheduler = createRafScheduler(updateFromScroll);
71
72  const refreshSize = () => {
73    headerHeight = header.offsetHeight;
74    resetDirection();
75    if (previousY <= headerHeight || isInteractionActive()) {
76      setHidden(false);
77    }
78
79    syncHeaderOffset?.();
80  };
81
82  const restorePosition = () => {
83    container.classList.remove('is-scroll-ready');
84    refreshSize();
85    setHidden(previousY > headerHeight && !isInteractionActive());
86    // Measure the initial/restored state before enabling transitions.
87    container.getBoundingClientRect();
88    container.classList.add('is-scroll-ready');
89  };
90
91  const updateInteraction = () => {
92    resetDirection();
93    if (isInteractionActive()) {
94      setHidden(false);
95    }
96
97    syncHeaderOffset?.();
98  };
99
100  const interactionScheduler = createRafScheduler(updateInteraction);
101  header.addEventListener('focusin', updateInteraction);
102  header.addEventListener('focusout', interactionScheduler.schedule);
103
104  const menuObserver = new MutationObserver(interactionScheduler.schedule);
105  menuObserver.observe(header, {subtree: true, attributes: true, attributeFilter: ['class', 'aria-expanded']});
106  const searchObserver = new MutationObserver(interactionScheduler.schedule);
107  searchObserver.observe(document.body, {attributes: true, attributeFilter: ['class']});
108
109  if (typeof ResizeObserver !== 'undefined') {
110    const resizeObserver = new ResizeObserver(refreshSize);
111    resizeObserver.observe(header);
112  }
113
114  window.addEventListener('scroll', scrollScheduler.schedule, {passive: true});
115  window.addEventListener('resize', refreshSize);
116  window.addEventListener('orientationchange', refreshSize);
117  window.addEventListener('pageshow', restorePosition);
118  window.addEventListener('pagehide', scrollScheduler.cancel);
119  container.classList.add('site-header-container--scroll-reveal');
120  restorePosition();
121};

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.