UNPKG

@codingheads/sticky-header

Version:

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

280 lines (202 loc) 11.1 kB
"use strict"; /**! * stickyHeader - JS plugin to create sticky header * created by Bogdan Barbu * requires IntersectionObserver * * Author: Bogdan Barbu * Team: Codingheads (codingheads.com) * * @format */ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 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; } } Object.defineProperty(exports, "__esModule", { value: true }); 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(); var StickyHeader = function StickyHeader(_element2) { var _this = this; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$pinnedClass = _ref.pinnedClass, pinnedClass = _ref$pinnedClass === void 0 ? 'sticky-pinned' : _ref$pinnedClass, _ref$unpinnedClass = _ref.unpinnedClass, unpinnedClass = _ref$unpinnedClass === void 0 ? 'sticky-unpinned' : _ref$unpinnedClass, _ref$mainClass = _ref.mainClass, mainClass = _ref$mainClass === void 0 ? 'sticky' : _ref$mainClass, _ref$offset = _ref.offset, offset = _ref$offset === void 0 ? 0 : _ref$offset, _ref$positionStickyWo = _ref.positionStickyWorkaround, positionStickyWorkaround = _ref$positionStickyWo === void 0 ? true : _ref$positionStickyWo, _ref$addBodyClasses = _ref.addBodyClasses, addBodyClasses = _ref$addBodyClasses === void 0 ? true : _ref$addBodyClasses; _classCallCheck(this, StickyHeader); _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: function value(entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) { window.requestAnimationFrame(function () { [_classPrivateFieldGet(_this, _element)].concat(_toConsumableArray(_classPrivateFieldGet(_this, _addBodyClasses) ? [document.body] : [])).forEach(function (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(function () { [_classPrivateFieldGet(_this, _element)].concat(_toConsumableArray(_classPrivateFieldGet(_this, _addBodyClasses) ? [document.body] : [])).forEach(function (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() { var _this2 = this; var parent = _classPrivateFieldGet(this, _element).parentElement; var intersectionItem = document.createElement('div'); var 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 var stickyPosition = window.getComputedStyle(_classPrivateFieldGet(this, _element), null).getPropertyValue('position'); if (!_classPrivateFieldGet(this, _offset) || stickyPosition == 'sticky') { _classPrivateFieldSet(this, _positionStickyWorkaround, true); } var height = _classPrivateFieldGet(this, _element).clientHeight; var 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'; var 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 var updateOffset = function updateOffset(newHeight) { if (newHeight) height = newHeight; intersectionItemOffset.style.top = _classPrivateFieldGet(_this2, _offset) - height + 'px'; }; ['resize', 'orientationchange'].forEach(function (type) { return window.addEventListener(type, function () { updateOffset(_classPrivateFieldGet(_this2, _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); } exports["default"] = StickyHeader; window.StickyHeader = StickyHeader; // register jQuery plugin if jQuery is available if ('jQuery' in window) { window.jQuery.fn.stickyHeader = function (options) { this.each(function (_i, element) { return new StickyHeader(element, options); }); return this; }; }