diff options
Diffstat (limited to 'js/src/util')
-rw-r--r-- | js/src/util/scrollbar.js | 121 |
1 files changed, 67 insertions, 54 deletions
diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 79a3b12c78..e23415f1dc 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -7,78 +7,91 @@ import SelectorEngine from '../dom/selector-engine' import Manipulator from '../dom/manipulator' +import { isElement } from './index' const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' const SELECTOR_STICKY_CONTENT = '.sticky-top' -const getWidth = () => { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth - return Math.abs(window.innerWidth - documentWidth) -} +class ScrollBarHelper { + constructor() { + this._element = document.body + } -const hide = (width = getWidth()) => { - _disableOverFlow() - // give padding to element to balances the hidden scrollbar width - _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) - // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth - _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) - _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) -} + getWidth() { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth + return Math.abs(window.innerWidth - documentWidth) + } -const _disableOverFlow = () => { - const actualValue = document.body.style.overflow - if (actualValue) { - Manipulator.setDataAttribute(document.body, 'overflow', actualValue) + hide() { + const width = this.getWidth() + this._disableOverFlow() + // give padding to element to balance the hidden scrollbar width + this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width) + // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth + this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) + this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) } - document.body.style.overflow = 'hidden' -} + _disableOverFlow() { + this._saveInitialAttribute(this._element, 'overflow') + this._element.style.overflow = 'hidden' + } -const _setElementAttributes = (selector, styleProp, callback) => { - const scrollbarWidth = getWidth() - SelectorEngine.find(selector) - .forEach(element => { - if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) { + _setElementAttributes(selector, styleProp, callback) { + const scrollbarWidth = this.getWidth() + const manipulationCallBack = element => { + if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { return } - const actualValue = element.style[styleProp] - if (actualValue) { - Manipulator.setDataAttribute(element, styleProp, actualValue) - } - + this._saveInitialAttribute(element, styleProp) const calculatedValue = window.getComputedStyle(element)[styleProp] element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px` - }) -} + } -const reset = () => { - _resetElementAttributes('body', 'overflow') - _resetElementAttributes('body', 'paddingRight') - _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') - _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') -} + this._applyManipulationCallback(selector, manipulationCallBack) + } + + reset() { + this._resetElementAttributes(this._element, 'overflow') + this._resetElementAttributes(this._element, 'paddingRight') + this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') + this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') + } + + _saveInitialAttribute(element, styleProp) { + const actualValue = element.style[styleProp] + if (actualValue) { + Manipulator.setDataAttribute(element, styleProp, actualValue) + } + } + + _resetElementAttributes(selector, styleProp) { + const manipulationCallBack = element => { + const value = Manipulator.getDataAttribute(element, styleProp) + if (typeof value === 'undefined') { + element.style.removeProperty(styleProp) + } else { + Manipulator.removeDataAttribute(element, styleProp) + element.style[styleProp] = value + } + } -const _resetElementAttributes = (selector, styleProp) => { - SelectorEngine.find(selector).forEach(element => { - const value = Manipulator.getDataAttribute(element, styleProp) - if (typeof value === 'undefined') { - element.style.removeProperty(styleProp) + this._applyManipulationCallback(selector, manipulationCallBack) + } + + _applyManipulationCallback(selector, callBack) { + if (isElement(selector)) { + callBack(selector) } else { - Manipulator.removeDataAttribute(element, styleProp) - element.style[styleProp] = value + SelectorEngine.find(selector, this._element).forEach(callBack) } - }) -} + } -const isBodyOverflowing = () => { - return getWidth() > 0 + isOverflowing() { + return this.getWidth() > 0 + } } -export { - getWidth, - hide, - isBodyOverflowing, - reset -} +export default ScrollBarHelper |