Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 2x 15x 15x 15x 2x 15x 15x 15x 15x 15x 2x 2x 2x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 2x | import { isBrowser } from './util'; /** Internal {@link ScrollToOptions | `ScrollToOptions`}: `left` and `top` properties always have values */ interface _ScrollPosition extends ScrollToOptions { left: number; top: number; } export interface ScrollPositionElement extends ScrollToOptions { /** * A valid CSS selector. Some special characters need to be escaped (https://mathiasbynens.be/notes/css-escapes). * @example * Here are some examples: * * - `.title` * - `.content:first-child` * - `#marker` * - `#marker\~with\~symbols` * - `#marker.with.dot`: Selects `class="with dot" id="marker"`, not `id="marker.with.dot"` * */ el: string | Element; } /** Scroll parameters */ export type ScrollPosition = ScrollToOptions | ScrollPositionElement; /** Get current window scroll position */ export const winScrollPos = (): _ScrollPosition => ({ left: window.scrollX, top: window.scrollY }); /** Get element position for scrolling in document */ function getElementPosition( el: Element, offset: ScrollToOptions ): _ScrollPosition { const docRect = document.documentElement.getBoundingClientRect(); const elRect = el.getBoundingClientRect(); return { behavior: offset.behavior, left: elRect.left - docRect.left - (offset.left || 0), top: elRect.top - docRect.top - (offset.top || 0) }; } /** Scroll to specified position */ export function scrollToPosition(position: ScrollPosition): void { if ('el' in position) { const positionEl = position.el; const el = typeof positionEl === 'string' ? document.querySelector(positionEl) : positionEl; if (!el) return; position = getElementPosition(el, position); } if ('scrollBehavior' in document.documentElement.style) { window.scrollTo(position); } else { window.scrollTo( Number.isFinite(position.left) ? position.left! : window.scrollX, Number.isFinite(position.top) ? position.top! : window.scrollY ); } } /** Stored scroll positions */ export const scrollPositions = new Map<string, _ScrollPosition>(); const POSITION_KEY = '__scroll_position_key'; /** Save scroll position */ export function saveScrollPosition( key: string, scrollPosition = winScrollPos() ) { scrollPosition = { ...scrollPosition }; scrollPositions.set(key, scrollPosition); try { if (location.href !== key) return; // preserve the existing history state as it could be overridden by the user const stateCopy = { ...(history.state || {}), [POSITION_KEY]: scrollPosition }; history.replaceState(stateCopy, ''); } catch (error) {} } /** Get saved scroll position */ export function getSavedScrollPosition( key: string, defaultValue: _ScrollPosition | null = null ): _ScrollPosition | null { const scroll = scrollPositions.get(key) || history.state[POSITION_KEY]; // Saved scroll position should not be used multiple times, next time should use newly saved position scrollPositions.delete(key); return scroll || defaultValue; } |