Created
September 16, 2024 20:10
-
-
Save JPBM135/78062bad0281ed5dd9f6baf020da7545 to your computer and use it in GitHub Desktop.
Block Scroll and Scrollbar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
37 - ArrowUp | |
38 - ArrowDown | |
39 - ArrowRight | |
40 - ArrowLeft | |
32 - Space | |
33 - PageUp | |
34 - PageDown | |
35 - End | |
36 - Home | |
*/ | |
const SCROLL_RELATED_KEYS = new Set([37, 38, 39, 40, 32, 33, 34, 35, 36]); | |
const SINGLETON_INSTANCE: { | |
disableScroll: (() => void) | null; | |
enableScroll: (() => void) | null; | |
} = { | |
disableScroll: null, | |
enableScroll: null, | |
}; | |
// This variable is used to override the scrollbar display, see `scrollbar.scss` | |
const SCROLLBAR_OVERRIDE_VARIABLE = | |
'--override-scrollbar-display'; | |
function checkIfBrowserSupportsPassive() { | |
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#safely_detecting_option_support | |
let passiveSupported = false; | |
try { | |
const options = { | |
get passive() { | |
// This function will be called when the browser | |
// attempts to access the passive property. | |
passiveSupported = true; | |
return false; | |
}, | |
} as EventListenerOptions; | |
window.addEventListener( | |
'test' as unknown as keyof WindowEventMap, | |
null as unknown as EventListener, | |
options, | |
); | |
window.removeEventListener( | |
'test' as unknown as keyof WindowEventMap, | |
null as unknown as EventListener, | |
options, | |
); | |
} catch (err) { | |
passiveSupported = false; | |
} | |
return passiveSupported; | |
} | |
function determineScrollEventOptionsAndName(isPassiveSupported: boolean) { | |
// Chrome and Firefox on Android support passive event listeners | |
const scrollEventListenerOptions: EventListenerOptions | boolean = | |
isPassiveSupported ? ({ passive: false } as EventListenerOptions) : false; | |
const testDiv = document.createElement('div'); | |
const scrollEventName = ('onwheel' in testDiv | |
? 'wheel' | |
: 'mousewheel') as unknown as keyof WindowEventMap; | |
// Schedule the removal of the test div for the next tick | |
setTimeout(() => { | |
testDiv.remove(); | |
}); | |
return { | |
scrollEventName, | |
scrollEventListenerOptions, | |
}; | |
} | |
interface ToggleWindowEventOptions { | |
eventName: keyof WindowEventMap; | |
handler: EventListener | ((e: KeyboardEvent) => void); | |
options?: EventListenerOptions | boolean; | |
} | |
function toggleWindowEvents( | |
events: ToggleWindowEventOptions[], | |
type: 'add' | 'remove', | |
) { | |
for (const event of events) { | |
if (type === 'add') { | |
window.addEventListener( | |
event.eventName, | |
event.handler as EventListener, | |
event.options, | |
); | |
continue; | |
} | |
window.removeEventListener( | |
event.eventName, | |
event.handler as EventListener, | |
event.options, | |
); | |
} | |
} | |
function createScrollToggle() { | |
if (typeof window === 'undefined') { | |
throw new Error('This function should be called in the browser'); | |
} | |
// Helper function for disabling scroll | |
const preventDefault = (e: Event) => { | |
e.preventDefault(); | |
}; | |
const preventDefaultForScrollKeys = (e: KeyboardEvent) => { | |
if (!SCROLL_RELATED_KEYS.has(Number(e.code))) return; | |
e.preventDefault(); | |
}; | |
const passiveSupported = checkIfBrowserSupportsPassive(); | |
const { scrollEventName, scrollEventListenerOptions } = | |
determineScrollEventOptionsAndName(passiveSupported); | |
const WINDOW_EVENTS: ToggleWindowEventOptions[] = [ | |
{ | |
eventName: 'DOMMouseScroll' as unknown as keyof WindowEventMap, | |
handler: preventDefault, | |
options: false, | |
}, | |
{ | |
eventName: scrollEventName, | |
handler: preventDefault, | |
options: scrollEventListenerOptions, | |
}, | |
{ | |
eventName: 'touchmove', | |
handler: preventDefault, | |
options: scrollEventListenerOptions, | |
}, | |
{ | |
eventName: 'keydown', | |
handler: preventDefaultForScrollKeys, | |
}, | |
]; | |
const disableScroll = () => { | |
toggleWindowEvents(WINDOW_EVENTS, 'add'); | |
document.documentElement.style.setProperty( | |
PALANTIR_SCROLLBAR_OVERRIDE_VARIABLE, | |
'none', | |
); | |
}; | |
// call this to Enable | |
const enableScroll = () => { | |
toggleWindowEvents(WINDOW_EVENTS, 'remove'); | |
document.documentElement.style.removeProperty( | |
PALANTIR_SCROLLBAR_OVERRIDE_VARIABLE, | |
); | |
}; | |
return { | |
disableScroll, | |
enableScroll, | |
}; | |
} | |
export function toggleScroll() { | |
if (!SINGLETON_INSTANCE.disableScroll) { | |
const { disableScroll, enableScroll } = createScrollToggle(); | |
SINGLETON_INSTANCE.disableScroll = disableScroll; | |
SINGLETON_INSTANCE.enableScroll = enableScroll; | |
} | |
return SINGLETON_INSTANCE as { | |
disableScroll: () => void; | |
enableScroll: () => void; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment