@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
JavaScript
/**!
* 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;
};
}
;