UNPKG

@codingheads/sticky-header

Version:

A library that allows you to create sticky headers. It uses `position: sticky` and IntersectionObserver

245 lines (179 loc) 8.91 kB
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); } function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; } function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; } function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); } function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } } var _pinnedClass = /*#__PURE__*/new WeakMap(); var _unpinnedClass = /*#__PURE__*/new WeakMap(); var _mainClass = /*#__PURE__*/new WeakMap(); var _offset = /*#__PURE__*/new WeakMap(); var _positionStickyWorkaround = /*#__PURE__*/new WeakMap(); var _noNativeSupport = /*#__PURE__*/new WeakMap(); var _element = /*#__PURE__*/new WeakMap(); var _observer = /*#__PURE__*/new WeakMap(); var _addBodyClasses = /*#__PURE__*/new WeakMap(); var _init = /*#__PURE__*/new WeakSet(); var _setSticky = /*#__PURE__*/new WeakMap(); /**! * stickyHeader - JS plugin to create sticky header * created by Bogdan Barbu * requires IntersectionObserver * * Author: Bogdan Barbu * Team: Codingheads (codingheads.com) * * @format */ export default class StickyHeader { constructor(_element2, { pinnedClass = 'sticky-pinned', unpinnedClass = 'sticky-unpinned', mainClass = 'sticky', offset = 0, positionStickyWorkaround = true, addBodyClasses = true } = {}) { _init.add(this); _pinnedClass.set(this, { writable: true, value: 'sticky-pinned' }); _unpinnedClass.set(this, { writable: true, value: 'sticky-unpinned' }); _mainClass.set(this, { writable: true, value: 'sticky' }); _offset.set(this, { writable: true, value: 0 }); _positionStickyWorkaround.set(this, { writable: true, value: true }); _noNativeSupport.set(this, { writable: true, value: false }); _element.set(this, { writable: true, value: null }); _observer.set(this, { writable: true, value: null }); _addBodyClasses.set(this, { writable: true, value: true }); _setSticky.set(this, { writable: true, value: entries => { entries.forEach(entry => { if (!entry.isIntersecting) { window.requestAnimationFrame(() => { [_classPrivateFieldGet(this, _element), ...(_classPrivateFieldGet(this, _addBodyClasses) ? [document.body] : [])].forEach(element => { element.classList.remove(_classPrivateFieldGet(this, _unpinnedClass)); element.classList.add(_classPrivateFieldGet(this, _pinnedClass)); }); if (_classPrivateFieldGet(this, _noNativeSupport) && _classPrivateFieldGet(this, _addBodyClasses)) { document.body.style.paddingTop = _classPrivateFieldGet(this, _element).clientHeight + 'px'; } // dispatch an event to tell that we're pinned _classPrivateFieldGet(this, _element).dispatchEvent(new CustomEvent('stickyIsPinned', { bubbles: true })); }); } else { window.requestAnimationFrame(() => { [_classPrivateFieldGet(this, _element), ...(_classPrivateFieldGet(this, _addBodyClasses) ? [document.body] : [])].forEach(element => { element.classList.add(_classPrivateFieldGet(this, _unpinnedClass)); element.classList.remove(_classPrivateFieldGet(this, _pinnedClass)); }); if (_classPrivateFieldGet(this, _noNativeSupport) && _classPrivateFieldGet(this, _addBodyClasses)) { document.body.style.paddingTop = '0'; } // dispatch an event to tell that we're unpinned _classPrivateFieldGet(this, _element).dispatchEvent(new CustomEvent('stickyIsUnpinned', { bubbles: true })); }); } }); } }); _classPrivateFieldSet(this, _element, _element2); _classPrivateFieldSet(this, _pinnedClass, pinnedClass); _classPrivateFieldSet(this, _unpinnedClass, unpinnedClass); _classPrivateFieldSet(this, _mainClass, mainClass); _classPrivateFieldSet(this, _offset, offset); _classPrivateFieldSet(this, _positionStickyWorkaround, positionStickyWorkaround); _classPrivateFieldSet(this, _addBodyClasses, addBodyClasses); // does this have native support (Modernizr test) _classPrivateFieldSet(this, _noNativeSupport, document.documentElement.classList.contains('no-csspositionsticky')); // initialize _classPrivateMethodGet(this, _init, _init2).call(this); } } function _init2() { const parent = _classPrivateFieldGet(this, _element).parentElement; const intersectionItem = document.createElement('div'); let containerPosition = window.getComputedStyle(parent).getPropertyValue('position'); _classPrivateFieldGet(this, _element).classList.add(_classPrivateFieldGet(this, _mainClass)); // add position: relative if the class doesn't add it if (!containerPosition || containerPosition == 'static') { parent.style.position = 'relative'; } // use the workaround if position is sticky or there's no offset set up const stickyPosition = window.getComputedStyle(_classPrivateFieldGet(this, _element), null).getPropertyValue('position'); if (!_classPrivateFieldGet(this, _offset) || stickyPosition == 'sticky') { _classPrivateFieldSet(this, _positionStickyWorkaround, true); } let height = _classPrivateFieldGet(this, _element).clientHeight; let toObserve = intersectionItem; // add the intersetion observer item intersectionItem.classList.add('sticky-observer'); intersectionItem.style.pointerEvents = 'none'; intersectionItem.style.visibility = 'hidden'; if (!_classPrivateFieldGet(this, _positionStickyWorkaround)) { // add the item to the top of the page intersectionItem.style.position = 'absolute'; intersectionItem.style.top = '0'; intersectionItem.style.left = '0'; intersectionItem.style.right = '0'; intersectionItem.style.height = _classPrivateFieldGet(this, _offset) + 'px'; parent.appendChild(intersectionItem); } else { // as a workaround for position: sticky issues, use an element right under the header intersectionItem.style.position = 'relative'; intersectionItem.style.height = '1px'; if (_classPrivateFieldGet(this, _offset)) { // if we are using an offset, adjust the "intersection" object position intersectionItem.style.height = '0'; const intersectionItemOffset = document.createElement('div'); intersectionItemOffset.classList.add('sticky-observer-offset'); intersectionItemOffset.style.position = 'absolute'; intersectionItemOffset.style.height = '1px'; intersectionItemOffset.style.left = '0'; intersectionItemOffset.style.right = '0'; intersectionItemOffset.style.top = _classPrivateFieldGet(this, _offset) - height + 'px'; intersectionItem.appendChild(intersectionItemOffset); toObserve = intersectionItemOffset; // update the offset when the layout changes const updateOffset = newHeight => { if (newHeight) height = newHeight; intersectionItemOffset.style.top = _classPrivateFieldGet(this, _offset) - height + 'px'; }; ['resize', 'orientationchange'].forEach(type => window.addEventListener(type, () => { updateOffset(_classPrivateFieldGet(this, _element).clientHeight); })); } parent.insertBefore(intersectionItem, _classPrivateFieldGet(this, _element).nextElementSibling); } // create the observer _classPrivateFieldSet(this, _observer, new IntersectionObserver(_classPrivateFieldGet(this, _setSticky))); _classPrivateFieldGet(this, _observer).observe(toObserve); } window.StickyHeader = StickyHeader; // register jQuery plugin if jQuery is available if ('jQuery' in window) { window.jQuery.fn.stickyHeader = function (options) { this.each((_i, element) => new StickyHeader(element, options)); return this; }; }