From 9510081861128eef45776564de48240c07f2373e Mon Sep 17 00:00:00 2001 From: drebot92 Date: Tue, 1 Aug 2023 18:06:36 +0300 Subject: [PATCH] Added nativeSticky and scrollContainerSelector options --- README.md | 28 +- bower.json | 2 +- dist/jquery.sticky-sidebar.js | 1550 +++++++++++++++-------------- dist/jquery.sticky-sidebar.js.map | 2 +- dist/jquery.sticky-sidebar.min.js | 4 +- dist/sticky-sidebar.js | 1435 +++++++++++++------------- dist/sticky-sidebar.js.map | 2 +- dist/sticky-sidebar.min.js | 4 +- package.json | 2 +- src/sticky-sidebar.js | 403 ++++---- 10 files changed, 1812 insertions(+), 1620 deletions(-) diff --git a/README.md b/README.md index 136460d..49b491f 100644 --- a/README.md +++ b/README.md @@ -42,20 +42,26 @@ npm install sticky-sidebar Your website's html structure has to be similar to this in order to work: ````html -
- -
- -
+
+
+ +
+ +
+
```` Note that inner sidebar wrapper ``.sidebar__innner`` is optional but highly recommended, if you don't write it yourself, the script will create one for you under class name ``inner-wrapper-sticky``. but this may cause many problems. +If your content is inside a container with scroll, this must be specified with the ``scrollContainerSelector`` option. + +If you need native CSS sticky, use the ``nativeSticky`` option. + For the above example, you can use the following JavaScript: ````html @@ -65,8 +71,10 @@ For the above example, you can use the following JavaScript: var sidebar = new StickySidebar('.sidebar', { topSpacing: 20, bottomSpacing: 20, + scrollContainerSelector: '.scroll-container', containerSelector: '.main-content', - innerWrapperSelector: '.sidebar__inner' + innerWrapperSelector: '.sidebar__inner', + nativeSticky: true }); ```` diff --git a/bower.json b/bower.json index dc3a48a..6c551ce 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "sticky-sidebar", "description": "A JavaScript plugin for making smart and high performance.", - "version": "3.3.1", + "version": "3.3.5", "authors": [ "Ahmed Bouhuolia " ], diff --git a/dist/jquery.sticky-sidebar.js b/dist/jquery.sticky-sidebar.js index f60442b..2582582 100644 --- a/dist/jquery.sticky-sidebar.js +++ b/dist/jquery.sticky-sidebar.js @@ -1,750 +1,810 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global.StickySidebar = factory()); -}(this, (function () { 'use strict'; - -var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - - -function unwrapExports (x) { - return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; -} - -function createCommonjsModule(fn, module) { - return module = { exports: {} }, fn(module, module.exports), module.exports; -} - -var stickySidebar = createCommonjsModule(function (module, exports) { -(function (global, factory) { - if (typeof undefined === "function" && undefined.amd) { - undefined(['exports'], factory); - } else { - factory(exports); - } -})(commonjsGlobal, function (exports) { - Object.defineProperty(exports, "__esModule", { - value: true - }); - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - var _createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - - /** - * Sticky Sidebar JavaScript Plugin. - * @version 3.3.4 - * @author Ahmed Bouhuolia - * @license The MIT License (MIT) - */ - var StickySidebar = function () { - - // --------------------------------- - // # Define Constants - // --------------------------------- - // - var EVENT_KEY = '.stickySidebar'; - var DEFAULTS = { - /** - * Additional top spacing of the element when it becomes sticky. - * @type {Numeric|Function} - */ - topSpacing: 0, - - /** - * Additional bottom spacing of the element when it becomes sticky. - * @type {Numeric|Function} - */ - bottomSpacing: 0, - - /** - * Container sidebar selector to know what the beginning and end of sticky element. - * @type {String|False} - */ - containerSelector: false, - - /** - * Inner wrapper selector. - * @type {String} - */ - innerWrapperSelector: '.inner-wrapper-sticky', - - /** - * The name of CSS class to apply to elements when they have become stuck. - * @type {String|False} - */ - stickyClass: 'is-affixed', - - /** - * Detect when sidebar and its container change height so re-calculate their dimensions. - * @type {Boolean} - */ - resizeSensor: true, - - /** - * The sidebar returns to its normal position if its width below this value. - * @type {Numeric} - */ - minWidth: false - }; - - // --------------------------------- - // # Class Definition - // --------------------------------- - // - /** - * Sticky Sidebar Class. - * @public - */ - - var StickySidebar = function () { - - /** - * Sticky Sidebar Constructor. - * @constructor - * @param {HTMLElement|String} sidebar - The sidebar element or sidebar selector. - * @param {Object} options - The options of sticky sidebar. - */ - function StickySidebar(sidebar) { - var _this = this; - - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, StickySidebar); - - this.options = StickySidebar.extend(DEFAULTS, options); - - // Sidebar element query if there's no one, throw error. - this.sidebar = 'string' === typeof sidebar ? document.querySelector(sidebar) : sidebar; - if ('undefined' === typeof this.sidebar) throw new Error("There is no specific sidebar element."); - - this.sidebarInner = false; - this.container = this.sidebar.parentElement; - - // Current Affix Type of sidebar element. - this.affixedType = 'STATIC'; - this.direction = 'down'; - this.support = { - transform: false, - transform3d: false - }; - - this._initialized = false; - this._reStyle = false; - this._breakpoint = false; - - // Dimensions of sidebar, container and screen viewport. - this.dimensions = { - translateY: 0, - maxTranslateY: 0, - topSpacing: 0, - lastTopSpacing: 0, - bottomSpacing: 0, - lastBottomSpacing: 0, - sidebarHeight: 0, - sidebarWidth: 0, - containerTop: 0, - containerHeight: 0, - viewportHeight: 0, - viewportTop: 0, - lastViewportTop: 0 - }; - - // Bind event handlers for referencability. - ['handleEvent'].forEach(function (method) { - _this[method] = _this[method].bind(_this); - }); - - // Initialize sticky sidebar for first time. - this.initialize(); - } - - /** - * Initializes the sticky sidebar by adding inner wrapper, define its container, - * min-width breakpoint, calculating dimensions, adding helper classes and inline style. - * @private - */ - - - _createClass(StickySidebar, [{ - key: 'initialize', - value: function initialize() { - var _this2 = this; - - this._setSupportFeatures(); - - // Get sticky sidebar inner wrapper, if not found, will create one. - if (this.options.innerWrapperSelector) { - this.sidebarInner = this.sidebar.querySelector(this.options.innerWrapperSelector); - - if (null === this.sidebarInner) this.sidebarInner = false; - } - - if (!this.sidebarInner) { - var wrapper = document.createElement('div'); - wrapper.setAttribute('class', 'inner-wrapper-sticky'); - this.sidebar.appendChild(wrapper); - - while (this.sidebar.firstChild != wrapper) { - wrapper.appendChild(this.sidebar.firstChild); - }this.sidebarInner = this.sidebar.querySelector('.inner-wrapper-sticky'); - } - - // Container wrapper of the sidebar. - if (this.options.containerSelector) { - var containers = document.querySelectorAll(this.options.containerSelector); - containers = Array.prototype.slice.call(containers); - - containers.forEach(function (container, item) { - if (!container.contains(_this2.sidebar)) return; - _this2.container = container; - }); - - if (!containers.length) throw new Error("The container does not contains on the sidebar."); - } - - // If top/bottom spacing is not function parse value to integer. - if ('function' !== typeof this.options.topSpacing) this.options.topSpacing = parseInt(this.options.topSpacing) || 0; - - if ('function' !== typeof this.options.bottomSpacing) this.options.bottomSpacing = parseInt(this.options.bottomSpacing) || 0; - - // Breakdown sticky sidebar if screen width below `options.minWidth`. - this._widthBreakpoint(); - - // Calculate dimensions of sidebar, container and viewport. - this.calcDimensions(); - - // Affix sidebar in proper position. - this.stickyPosition(); - - // Bind all events. - this.bindEvents(); - - // Inform other properties the sticky sidebar is initialized. - this._initialized = true; - } - }, { - key: 'bindEvents', - value: function bindEvents() { - window.addEventListener('resize', this, { passive: true, capture: false }); - window.addEventListener('scroll', this, { passive: true, capture: false }); - - this.sidebar.addEventListener('update' + EVENT_KEY, this); - - if (this.options.resizeSensor && 'undefined' !== typeof ResizeSensor) { - new ResizeSensor(this.sidebarInner, this.handleEvent); - new ResizeSensor(this.container, this.handleEvent); - } - } - }, { - key: 'handleEvent', - value: function handleEvent(event) { - this.updateSticky(event); - } - }, { - key: 'calcDimensions', - value: function calcDimensions() { - if (this._breakpoint) return; - var dims = this.dimensions; - - // Container of sticky sidebar dimensions. - dims.containerTop = StickySidebar.offsetRelative(this.container).top; - dims.containerHeight = this.container.clientHeight; - dims.containerBottom = dims.containerTop + dims.containerHeight; - - // Sidebar dimensions. - dims.sidebarHeight = this.sidebarInner.offsetHeight; - dims.sidebarWidth = this.sidebarInner.offsetWidth; - - // Screen viewport dimensions. - dims.viewportHeight = window.innerHeight; - - // Maximum sidebar translate Y. - dims.maxTranslateY = dims.containerHeight - dims.sidebarHeight; - - this._calcDimensionsWithScroll(); - } - }, { - key: '_calcDimensionsWithScroll', - value: function _calcDimensionsWithScroll() { - var dims = this.dimensions; - - dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar).left; - - dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop; - dims.viewportBottom = dims.viewportTop + dims.viewportHeight; - dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft; - - dims.topSpacing = this.options.topSpacing; - dims.bottomSpacing = this.options.bottomSpacing; - - if ('function' === typeof dims.topSpacing) dims.topSpacing = parseInt(dims.topSpacing(this.sidebar)) || 0; - - if ('function' === typeof dims.bottomSpacing) dims.bottomSpacing = parseInt(dims.bottomSpacing(this.sidebar)) || 0; - - if ('VIEWPORT-TOP' === this.affixedType) { - // Adjust translate Y in the case decrease top spacing value. - if (dims.topSpacing < dims.lastTopSpacing) { - dims.translateY += dims.lastTopSpacing - dims.topSpacing; - this._reStyle = true; - } - } else if ('VIEWPORT-BOTTOM' === this.affixedType) { - // Adjust translate Y in the case decrease bottom spacing value. - if (dims.bottomSpacing < dims.lastBottomSpacing) { - dims.translateY += dims.lastBottomSpacing - dims.bottomSpacing; - this._reStyle = true; - } - } - - dims.lastTopSpacing = dims.topSpacing; - dims.lastBottomSpacing = dims.bottomSpacing; - } - }, { - key: 'isSidebarFitsViewport', - value: function isSidebarFitsViewport() { - var dims = this.dimensions; - var offset = this.scrollDirection === 'down' ? dims.lastBottomSpacing : dims.lastTopSpacing; - return this.dimensions.sidebarHeight + offset < this.dimensions.viewportHeight; - } - }, { - key: 'observeScrollDir', - value: function observeScrollDir() { - var dims = this.dimensions; - if (dims.lastViewportTop === dims.viewportTop) return; - - var furthest = 'down' === this.direction ? Math.min : Math.max; - - // If the browser is scrolling not in the same direction. - if (dims.viewportTop === furthest(dims.viewportTop, dims.lastViewportTop)) this.direction = 'down' === this.direction ? 'up' : 'down'; - } - }, { - key: 'getAffixType', - value: function getAffixType() { - this._calcDimensionsWithScroll(); - var dims = this.dimensions; - var colliderTop = dims.viewportTop + dims.topSpacing; - var affixType = this.affixedType; - - if (colliderTop <= dims.containerTop || dims.containerHeight <= dims.sidebarHeight) { - dims.translateY = 0; - affixType = 'STATIC'; - } else { - affixType = 'up' === this.direction ? this._getAffixTypeScrollingUp() : this._getAffixTypeScrollingDown(); - } - - // Make sure the translate Y is not bigger than container height. - dims.translateY = Math.max(0, dims.translateY); - dims.translateY = Math.min(dims.containerHeight, dims.translateY); - dims.translateY = Math.round(dims.translateY); - - dims.lastViewportTop = dims.viewportTop; - return affixType; - } - }, { - key: '_getAffixTypeScrollingDown', - value: function _getAffixTypeScrollingDown() { - var dims = this.dimensions; - var sidebarBottom = dims.sidebarHeight + dims.containerTop; - var colliderTop = dims.viewportTop + dims.topSpacing; - var colliderBottom = dims.viewportBottom - dims.bottomSpacing; - var affixType = this.affixedType; - - if (this.isSidebarFitsViewport()) { - if (dims.sidebarHeight + colliderTop >= dims.containerBottom) { - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; - } else if (colliderTop >= dims.containerTop) { - dims.translateY = colliderTop - dims.containerTop; - affixType = 'VIEWPORT-TOP'; - } - } else { - if (dims.containerBottom <= colliderBottom) { - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; - } else if (sidebarBottom + dims.translateY <= colliderBottom) { - dims.translateY = colliderBottom - sidebarBottom; - affixType = 'VIEWPORT-BOTTOM'; - } else if (dims.containerTop + dims.translateY <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { - affixType = 'VIEWPORT-UNBOTTOM'; - } - } - - return affixType; - } - }, { - key: '_getAffixTypeScrollingUp', - value: function _getAffixTypeScrollingUp() { - var dims = this.dimensions; - var sidebarBottom = dims.sidebarHeight + dims.containerTop; - var colliderTop = dims.viewportTop + dims.topSpacing; - var colliderBottom = dims.viewportBottom - dims.bottomSpacing; - var affixType = this.affixedType; - - if (colliderTop <= dims.translateY + dims.containerTop) { - dims.translateY = colliderTop - dims.containerTop; - affixType = 'VIEWPORT-TOP'; - } else if (dims.containerBottom <= colliderBottom) { - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; - } else if (!this.isSidebarFitsViewport()) { - - if (dims.containerTop <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { - affixType = 'VIEWPORT-UNBOTTOM'; - } - } - - return affixType; - } - }, { - key: '_getStyle', - value: function _getStyle(affixType) { - if ('undefined' === typeof affixType) return; - - var style = { inner: {}, outer: {} }; - var dims = this.dimensions; - - switch (affixType) { - case 'VIEWPORT-TOP': - style.inner = { position: 'fixed', top: dims.topSpacing, - left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth }; - break; - case 'VIEWPORT-BOTTOM': - style.inner = { position: 'fixed', top: 'auto', left: dims.sidebarLeft, - bottom: dims.bottomSpacing, width: dims.sidebarWidth }; - break; - case 'CONTAINER-BOTTOM': - case 'VIEWPORT-UNBOTTOM': - var translate = this._getTranslate(0, dims.translateY + 'px'); - - if (translate) style.inner = { transform: translate };else style.inner = { position: 'absolute', top: dims.translateY, width: dims.sidebarWidth }; - break; - } - - switch (affixType) { - case 'VIEWPORT-TOP': - case 'VIEWPORT-BOTTOM': - case 'VIEWPORT-UNBOTTOM': - case 'CONTAINER-BOTTOM': - style.outer = { height: dims.sidebarHeight, position: 'relative' }; - break; - } - - style.outer = StickySidebar.extend({ height: '', position: '' }, style.outer); - style.inner = StickySidebar.extend({ position: 'relative', top: '', left: '', - bottom: '', width: '', transform: '' }, style.inner); - - return style; - } - }, { - key: 'stickyPosition', - value: function stickyPosition(force) { - if (this._breakpoint) return; - - force = this._reStyle || force || false; - - var offsetTop = this.options.topSpacing; - var offsetBottom = this.options.bottomSpacing; - - var affixType = this.getAffixType(); - var style = this._getStyle(affixType); - - if ((this.affixedType != affixType || force) && affixType) { - var affixEvent = 'affix.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; - StickySidebar.eventTrigger(this.sidebar, affixEvent); - - if ('STATIC' === affixType) StickySidebar.removeClass(this.sidebar, this.options.stickyClass);else StickySidebar.addClass(this.sidebar, this.options.stickyClass); - - for (var key in style.outer) { - var unit = 'number' === typeof style.outer[key] ? 'px' : ''; - this.sidebar.style[key] = style.outer[key] + unit; - } - - for (var _key in style.inner) { - var _unit = 'number' === typeof style.inner[_key] ? 'px' : ''; - this.sidebarInner.style[_key] = style.inner[_key] + _unit; - } - - var affixedEvent = 'affixed.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; - StickySidebar.eventTrigger(this.sidebar, affixedEvent); - } else { - if (this._initialized) this.sidebarInner.style.left = style.inner.left; - } - - this.affixedType = affixType; - } - }, { - key: '_widthBreakpoint', - value: function _widthBreakpoint() { - - if (window.innerWidth <= this.options.minWidth) { - this._breakpoint = true; - this.affixedType = 'STATIC'; - - this.sidebar.removeAttribute('style'); - StickySidebar.removeClass(this.sidebar, this.options.stickyClass); - this.sidebarInner.removeAttribute('style'); - } else { - this._breakpoint = false; - } - } - }, { - key: 'updateSticky', - value: function updateSticky() { - var _this3 = this; - - var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - - if (this._running) return; - this._running = true; - - (function (eventType) { - requestAnimationFrame(function () { - switch (eventType) { - // When browser is scrolling and re-calculate just dimensions - // within scroll. - case 'scroll': - _this3._calcDimensionsWithScroll(); - _this3.observeScrollDir(); - _this3.stickyPosition(); - break; - - // When browser is resizing or there's no event, observe width - // breakpoint and re-calculate dimensions. - case 'resize': - default: - _this3._widthBreakpoint(); - _this3.calcDimensions(); - _this3.stickyPosition(true); - break; - } - _this3._running = false; - }); - })(event.type); - } - }, { - key: '_setSupportFeatures', - value: function _setSupportFeatures() { - var support = this.support; - - support.transform = StickySidebar.supportTransform(); - support.transform3d = StickySidebar.supportTransform(true); - } - }, { - key: '_getTranslate', - value: function _getTranslate() { - var y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - - if (this.support.transform3d) return 'translate3d(' + y + ', ' + x + ', ' + z + ')';else if (this.support.translate) return 'translate(' + y + ', ' + x + ')';else return false; - } - }, { - key: 'destroy', - value: function destroy() { - window.removeEventListener('resize', this, { capture: false }); - window.removeEventListener('scroll', this, { capture: false }); - - this.sidebar.classList.remove(this.options.stickyClass); - this.sidebar.style.minHeight = ''; - - this.sidebar.removeEventListener('update' + EVENT_KEY, this); - - var styleReset = { inner: {}, outer: {} }; - - styleReset.inner = { position: '', top: '', left: '', bottom: '', width: '', transform: '' }; - styleReset.outer = { height: '', position: '' }; - - for (var key in styleReset.outer) { - this.sidebar.style[key] = styleReset.outer[key]; - }for (var _key2 in styleReset.inner) { - this.sidebarInner.style[_key2] = styleReset.inner[_key2]; - }if (this.options.resizeSensor && 'undefined' !== typeof ResizeSensor) { - ResizeSensor.detach(this.sidebarInner, this.handleEvent); - ResizeSensor.detach(this.container, this.handleEvent); - } - } - }], [{ - key: 'supportTransform', - value: function supportTransform(transform3d) { - var result = false, - property = transform3d ? 'perspective' : 'transform', - upper = property.charAt(0).toUpperCase() + property.slice(1), - prefixes = ['Webkit', 'Moz', 'O', 'ms'], - support = document.createElement('support'), - style = support.style; - - (property + ' ' + prefixes.join(upper + ' ') + upper).split(' ').forEach(function (property, i) { - if (style[property] !== undefined) { - result = property; - return false; - } - }); - return result; - } - }, { - key: 'eventTrigger', - value: function eventTrigger(element, eventName, data) { - try { - var event = new CustomEvent(eventName, { detail: data }); - } catch (e) { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent(eventName, true, true, data); - } - element.dispatchEvent(event); - } - }, { - key: 'extend', - value: function extend(defaults, options) { - var results = {}; - for (var key in defaults) { - if ('undefined' !== typeof options[key]) results[key] = options[key];else results[key] = defaults[key]; - } - return results; - } - }, { - key: 'offsetRelative', - value: function offsetRelative(element) { - var result = { left: 0, top: 0 }; - - do { - var offsetTop = element.offsetTop; - var offsetLeft = element.offsetLeft; - - if (!isNaN(offsetTop)) result.top += offsetTop; - - if (!isNaN(offsetLeft)) result.left += offsetLeft; - - element = 'BODY' === element.tagName ? element.parentElement : element.offsetParent; - } while (element); - return result; - } - }, { - key: 'addClass', - value: function addClass(element, className) { - if (!StickySidebar.hasClass(element, className)) { - if (element.classList) element.classList.add(className);else element.className += ' ' + className; - } - } - }, { - key: 'removeClass', - value: function removeClass(element, className) { - if (StickySidebar.hasClass(element, className)) { - if (element.classList) element.classList.remove(className);else element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); - } - } - }, { - key: 'hasClass', - value: function hasClass(element, className) { - if (element.classList) return element.classList.contains(className);else return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className); - } - }, { - key: 'defaults', - get: function () { - return DEFAULTS; - } - }]); - - return StickySidebar; - }(); - - return StickySidebar; - }(); - - exports.default = StickySidebar; - - - // Global - // ------------------------- - window.StickySidebar = StickySidebar; -}); -}); - -unwrapExports(stickySidebar); - -var jquery_stickySidebar = createCommonjsModule(function (module, exports) { -(function (global, factory) { - if (typeof undefined === "function" && undefined.amd) { - undefined(['./sticky-sidebar'], factory); - } else { - factory(stickySidebar); - } -})(commonjsGlobal, function (_stickySidebar) { - var _stickySidebar2 = _interopRequireDefault(_stickySidebar); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - (function () { - if ('undefined' === typeof window) return; - - var plugin = window.$ || window.jQuery || window.Zepto; - var DATA_NAMESPACE = 'stickySidebar'; - - // Make sure the site has jquery or zepto plugin. - if (plugin) { - var _jQueryPlugin = function (config) { - return this.each(function () { - var $this = plugin(this), - data = plugin(this).data(DATA_NAMESPACE); - - if (!data) { - data = new _stickySidebar2.default(this, typeof config == 'object' && config); - $this.data(DATA_NAMESPACE, data); - } - - if ('string' === typeof config) { - if (data[config] === undefined && ['destroy', 'updateSticky'].indexOf(config) === -1) throw new Error('No method named "' + config + '"'); - - data[config](); - } - }); - }; - - plugin.fn.stickySidebar = _jQueryPlugin; - plugin.fn.stickySidebar.Constructor = _stickySidebar2.default; - - var old = plugin.fn.stickySidebar; - - /** - * Sticky Sidebar No Conflict. - */ - plugin.fn.stickySidebar.noConflict = function () { - plugin.fn.stickySidebar = old; - return this; - }; - } - })(); -}); -}); - -var jquery_stickySidebar$1 = unwrapExports(jquery_stickySidebar); - -return jquery_stickySidebar$1; - -}))); + (global = global || self, global.StickySidebar = factory()); +}(this, function () { 'use strict'; + + var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function unwrapExports (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var stickySidebar = createCommonjsModule(function (module, exports) { + (function (global, factory) { + { + factory(exports); + } + })(commonjsGlobal, function (exports) { + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + /** + * Sticky Sidebar JavaScript Plugin. + * @version 3.3.5 + * @author Ahmed Bouhuolia + * @license The MIT License (MIT) + */ + var StickySidebar = function () { + + // --------------------------------- + // # Define Constants + // --------------------------------- + // + var EVENT_KEY = '.stickySidebar'; + + var DEFAULTS = { + /** + * Additional top spacing of the element when it becomes sticky. + * @type {Numeric|Function} + */ + topSpacing: 0, + + /** + * Additional bottom spacing of the element when it becomes sticky. + * @type {Numeric|Function} + */ + bottomSpacing: 0, + + /** + * Scroll container selector. + * @type {String|False} + */ + scrollContainerSelector: false, + + /** + * Container sidebar selector to know what the beginning and end of sticky element. + * @type {String|False} + */ + containerSelector: false, + + /** + * Inner wrapper selector. + * @type {String} + */ + innerWrapperSelector: '.inner-wrapper-sticky', + + /** + * The name of CSS class to apply to elements when they have become stuck. + * @type {String|False} + */ + stickyClass: 'is-affixed', + + /** + * Detect when sidebar and its container change height so re-calculate their dimensions. + * @type {Boolean} + */ + resizeSensor: true, + + /** + * The sidebar returns to its normal position if its width below this value. + * @type {Numeric} + */ + minWidth: false, + + /** + * Use native CSS position sticky. + * @type {Boolean} + */ + nativeSticky: false + }; + + // --------------------------------- + // # Class Definition + // --------------------------------- + // + /** + * Sticky Sidebar Class. + * @public + */ + + var StickySidebar = function () { + + /** + * Sticky Sidebar Constructor. + * @constructor + * @param {HTMLElement|String} sidebar - The sidebar element or sidebar selector. + * @param {Object} options - The options of sticky sidebar. + */ + function StickySidebar(sidebar) { + var _this = this; + + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, StickySidebar); + + this.options = StickySidebar.extend(DEFAULTS, options); + + // Sidebar element query if there's no one, throw error. + this.sidebar = 'string' === typeof sidebar ? document.querySelector(sidebar) : sidebar; + if ('undefined' === typeof this.sidebar) throw new Error("There is no specific sidebar element."); + + this.sidebarInner = false; + this.container = this.sidebar.parentElement; + + // Get scroll container, if provided + this.scrollContainer = this.options.scrollContainerSelector ? 'string' === typeof this.options.scrollContainerSelector ? document.querySelector(this.options.scrollContainerSelector) : this.options.scrollContainerSelector : undefined; + + // Current Affix Type of sidebar element. + this.affixedType = 'STATIC'; + this.direction = 'down'; + this.support = { + transform: false, + transform3d: false + }; + + this._initialized = false; + this._reStyle = false; + this._breakpoint = false; + + // Dimensions of sidebar, container and screen viewport. + this.dimensions = { + translateY: 0, + maxTranslateY: 0, + topSpacing: 0, + lastTopSpacing: 0, + bottomSpacing: 0, + lastBottomSpacing: 0, + sidebarHeight: 0, + sidebarWidth: 0, + containerTop: 0, + containerHeight: 0, + viewportHeight: 0, + viewportTop: 0, + lastViewportTop: 0 + }; + + // Bind event handlers for referencability. + ['handleEvent'].forEach(function (method) { + _this[method] = _this[method].bind(_this); + }); + + // Initialize sticky sidebar for first time. + this.initialize(); + } + + /** + * Initializes the sticky sidebar by adding inner wrapper, define its container, + * min-width breakpoint, calculating dimensions, adding helper classes and inline style. + * @private + */ + + + _createClass(StickySidebar, [{ + key: 'initialize', + value: function initialize() { + var _this2 = this; + + this._setSupportFeatures(); + + // Get sticky sidebar inner wrapper, if not found, will create one. + if (this.options.innerWrapperSelector) { + this.sidebarInner = this.sidebar.querySelector(this.options.innerWrapperSelector); + + if (null === this.sidebarInner) this.sidebarInner = false; + } + + if (!this.sidebarInner) { + var wrapper = document.createElement('div'); + wrapper.setAttribute('class', 'inner-wrapper-sticky'); + this.sidebar.appendChild(wrapper); + + while (this.sidebar.firstChild != wrapper) { + wrapper.appendChild(this.sidebar.firstChild); + }this.sidebarInner = this.sidebar.querySelector('.inner-wrapper-sticky'); + } + + // Container wrapper of the sidebar. + if (this.options.containerSelector) { + var containers = document.querySelectorAll(this.options.containerSelector); + containers = Array.prototype.slice.call(containers); + + containers.forEach(function (container, item) { + if (!container.contains(_this2.sidebar)) return; + _this2.container = container; + }); + + if (!containers.length) throw new Error("The container does not contains on the sidebar."); + } + + // If top/bottom spacing is not function parse value to integer. + if ('function' !== typeof this.options.topSpacing) this.options.topSpacing = parseInt(this.options.topSpacing) || 0; + + if ('function' !== typeof this.options.bottomSpacing) this.options.bottomSpacing = parseInt(this.options.bottomSpacing) || 0; + + // Breakdown sticky sidebar if screen width below `options.minWidth`. + this._widthBreakpoint(); + + // Calculate dimensions of sidebar, container and viewport. + this.calcDimensions(); + + // Affix sidebar in proper position. + this.stickyPosition(); + + // Bind all events. + this.bindEvents(); + + // Inform other properties the sticky sidebar is initialized. + this._initialized = true; + } + }, { + key: 'bindEvents', + value: function bindEvents() { + var _this3 = this; + + window.addEventListener('resize', this, { passive: true, capture: false }); + (this.scrollContainer || window).addEventListener('scroll', this, { passive: true, capture: false }); + + this.sidebar.addEventListener('update' + EVENT_KEY, this); + + if (this.options.resizeSensor && 'undefined' !== typeof ResizeObserver) { + this.resizeObserver = new ResizeObserver(function () { + return _this3.handleEvent({ type: 'resize' }); + }); + this.resizeObserver.observe(this.sidebarInner); + this.resizeObserver.observe(this.container); + if (this.scrollContainer) this.resizeObserver.observe(this.scrollContainer); + } + } + }, { + key: 'handleEvent', + value: function handleEvent(event) { + this.updateSticky(event); + } + }, { + key: 'calcDimensions', + value: function calcDimensions() { + if (this._breakpoint) return; + var dims = this.dimensions; + + // Container of sticky sidebar dimensions. + dims.containerTop = StickySidebar.offsetRelative(this.container, this.scrollContainer).top; + dims.containerHeight = this.container.clientHeight; + dims.containerBottom = dims.containerTop + dims.containerHeight; + + // Sidebar dimensions. + dims.sidebarHeight = this.sidebarInner.offsetHeight; + dims.sidebarWidth = this.sidebarInner.offsetWidth; + + // Screen viewport dimensions. + if (this.scrollContainer) { + var scrollContainerPaddingTop = parseFloat(getComputedStyle(this.scrollContainer).paddingTop) || 0; + var scrollContainerPaddingBottom = parseFloat(getComputedStyle(this.scrollContainer).paddingBottom) || 0; + + dims.viewportHeight = this.scrollContainer.clientHeight - scrollContainerPaddingTop - scrollContainerPaddingBottom; + } else { + dims.viewportHeight = window.innerHeight; + } + + // Maximum sidebar translate Y. + dims.maxTranslateY = dims.containerHeight - dims.sidebarHeight; + + this._calcDimensionsWithScroll(); + } + }, { + key: '_calcDimensionsWithScroll', + value: function _calcDimensionsWithScroll() { + var dims = this.dimensions; + + dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar, this.scrollContainer).left; + + if (this.scrollContainer) { + var scrollContainerPaddingTop = parseFloat(getComputedStyle(this.scrollContainer).paddingTop) || 0; + var scrollContainerPaddingLeft = parseFloat(getComputedStyle(this.scrollContainer).paddingLeft) || 0; + + dims.viewportTop = this.scrollContainer.scrollTop + scrollContainerPaddingTop; + dims.viewportLeft = this.scrollContainer.scrollLeft + scrollContainerPaddingLeft; + } else { + dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop; + dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + } + dims.viewportBottom = dims.viewportTop + dims.viewportHeight; + + dims.topSpacing = this.options.topSpacing; + dims.bottomSpacing = this.options.bottomSpacing; + + if ('function' === typeof dims.topSpacing) dims.topSpacing = parseInt(dims.topSpacing(this.sidebar)) || 0; + + if ('function' === typeof dims.bottomSpacing) dims.bottomSpacing = parseInt(dims.bottomSpacing(this.sidebar)) || 0; + + if ('VIEWPORT-TOP' === this.affixedType) { + // Adjust translate Y in the case decrease top spacing value. + if (dims.topSpacing < dims.lastTopSpacing) { + dims.translateY += dims.lastTopSpacing - dims.topSpacing; + this._reStyle = true; + } + } else if ('VIEWPORT-BOTTOM' === this.affixedType) { + // Adjust translate Y in the case decrease bottom spacing value. + if (dims.bottomSpacing < dims.lastBottomSpacing) { + dims.translateY += dims.lastBottomSpacing - dims.bottomSpacing; + this._reStyle = true; + } + } + + dims.lastTopSpacing = dims.topSpacing; + dims.lastBottomSpacing = dims.bottomSpacing; + } + }, { + key: 'isSidebarFitsViewport', + value: function isSidebarFitsViewport() { + var dims = this.dimensions; + var offset = this.scrollDirection === 'down' ? dims.lastBottomSpacing : dims.lastTopSpacing; + return this.dimensions.sidebarHeight + offset < this.dimensions.viewportHeight; + } + }, { + key: 'observeScrollDir', + value: function observeScrollDir() { + var dims = this.dimensions; + if (dims.lastViewportTop === dims.viewportTop) return; + + var furthest = 'down' === this.direction ? Math.min : Math.max; + + // If the browser is scrolling not in the same direction. + if (dims.viewportTop === furthest(dims.viewportTop, dims.lastViewportTop)) this.direction = 'down' === this.direction ? 'up' : 'down'; + } + }, { + key: 'getAffixType', + value: function getAffixType() { + this._calcDimensionsWithScroll(); + var dims = this.dimensions; + var colliderTop = dims.viewportTop + dims.topSpacing; + var affixType = this.affixedType; + + if (colliderTop <= dims.containerTop || dims.containerHeight <= dims.sidebarHeight) { + dims.translateY = 0; + affixType = 'STATIC'; + } else { + affixType = 'up' === this.direction ? this._getAffixTypeScrollingUp() : this._getAffixTypeScrollingDown(); + } + + // Make sure the translate Y is not bigger than container height. + dims.translateY = Math.max(0, dims.translateY); + dims.translateY = Math.min(dims.containerHeight, dims.translateY); + dims.translateY = Math.round(dims.translateY); + + dims.lastViewportTop = dims.viewportTop; + return affixType; + } + }, { + key: '_getAffixTypeScrollingDown', + value: function _getAffixTypeScrollingDown() { + var dims = this.dimensions; + var sidebarBottom = dims.sidebarHeight + dims.containerTop; + var colliderTop = dims.viewportTop + dims.topSpacing; + var colliderBottom = dims.viewportBottom - dims.bottomSpacing; + var affixType = this.affixedType; + + if (this.isSidebarFitsViewport()) { + if (dims.sidebarHeight + colliderTop >= dims.containerBottom) { + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; + } else if (colliderTop >= dims.containerTop) { + dims.translateY = colliderTop - dims.containerTop; + affixType = 'VIEWPORT-TOP'; + } + } else { + if (dims.containerBottom <= colliderBottom) { + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; + } else if (sidebarBottom + dims.translateY <= colliderBottom) { + dims.translateY = colliderBottom - sidebarBottom; + affixType = 'VIEWPORT-BOTTOM'; + } else if (dims.containerTop + dims.translateY <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { + affixType = 'VIEWPORT-UNBOTTOM'; + } + } + + return affixType; + } + }, { + key: '_getAffixTypeScrollingUp', + value: function _getAffixTypeScrollingUp() { + var dims = this.dimensions; + var sidebarBottom = dims.sidebarHeight + dims.containerTop; + var colliderTop = dims.viewportTop + dims.topSpacing; + var colliderBottom = dims.viewportBottom - dims.bottomSpacing; + var affixType = this.affixedType; + + if (colliderTop <= dims.translateY + dims.containerTop) { + dims.translateY = colliderTop - dims.containerTop; + affixType = 'VIEWPORT-TOP'; + } else if (dims.containerBottom <= colliderBottom) { + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; + } else if (!this.isSidebarFitsViewport()) { + + if (dims.containerTop <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { + affixType = 'VIEWPORT-UNBOTTOM'; + } + } + + return affixType; + } + }, { + key: '_getStyle', + value: function _getStyle(affixType) { + if ('undefined' === typeof affixType) return; + + var style = { inner: {}, outer: {} }; + var dims = this.dimensions; + + if (this.options.nativeSticky) { + switch (affixType) { + case 'VIEWPORT-TOP': + style.inner = { position: 'sticky', top: dims.topSpacing }; + break; + case 'VIEWPORT-BOTTOM': + style.inner = { position: 'sticky', top: dims.viewportHeight - dims.sidebarHeight - dims.bottomSpacing }; + break; + case 'CONTAINER-BOTTOM': + style.inner = { position: 'sticky', top: '100%' }; + break; + case 'VIEWPORT-UNBOTTOM': + style.inner = { position: 'relative', top: dims.translateY }; + break; + } + + switch (affixType) { + case 'VIEWPORT-TOP': + case 'VIEWPORT-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + case 'CONTAINER-BOTTOM': + style.outer = { height: '100%', position: 'relative' }; + break; + } + } else { + switch (affixType) { + case 'VIEWPORT-TOP': + style.inner = { position: 'fixed', top: dims.topSpacing, + left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth }; + break; + case 'VIEWPORT-BOTTOM': + style.inner = { position: 'fixed', top: 'auto', left: dims.sidebarLeft, + bottom: dims.bottomSpacing, width: dims.sidebarWidth }; + break; + case 'CONTAINER-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + var translate = this._getTranslate(0, dims.translateY + 'px'); + + if (translate) style.inner = { transform: translate };else style.inner = { position: 'absolute', top: dims.translateY, width: dims.sidebarWidth }; + break; + } + + switch (affixType) { + case 'VIEWPORT-TOP': + case 'VIEWPORT-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + case 'CONTAINER-BOTTOM': + style.outer = { height: dims.sidebarHeight, position: 'relative' }; + break; + } + } + + style.outer = StickySidebar.extend({ height: '', position: '' }, style.outer); + style.inner = StickySidebar.extend({ position: 'relative', top: '', left: '', + bottom: '', width: '', transform: '' }, style.inner); + + return style; + } + }, { + key: 'stickyPosition', + value: function stickyPosition(force) { + if (this._breakpoint) return; + + force = this._reStyle || force || false; + + var offsetTop = this.options.topSpacing; + var offsetBottom = this.options.bottomSpacing; + + var affixType = this.getAffixType(); + var style = this._getStyle(affixType); + + if ((this.affixedType != affixType || force) && affixType) { + var affixEvent = 'affix.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; + StickySidebar.eventTrigger(this.sidebar, affixEvent); + + if ('STATIC' === affixType) StickySidebar.removeClass(this.sidebar, this.options.stickyClass);else StickySidebar.addClass(this.sidebar, this.options.stickyClass); + + for (var key in style.outer) { + var unit = 'number' === typeof style.outer[key] ? 'px' : ''; + this.sidebar.style[key] = style.outer[key] + unit; + } + + for (var _key in style.inner) { + var _unit = 'number' === typeof style.inner[_key] ? 'px' : ''; + this.sidebarInner.style[_key] = style.inner[_key] + _unit; + } + + var affixedEvent = 'affixed.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; + StickySidebar.eventTrigger(this.sidebar, affixedEvent); + } else { + if (this._initialized) this.sidebarInner.style.left = style.inner.left; + } + + this.affixedType = affixType; + } + }, { + key: '_widthBreakpoint', + value: function _widthBreakpoint() { + + if (window.innerWidth <= this.options.minWidth) { + this._breakpoint = true; + this.affixedType = 'STATIC'; + + this.sidebar.removeAttribute('style'); + StickySidebar.removeClass(this.sidebar, this.options.stickyClass); + this.sidebarInner.removeAttribute('style'); + } else { + this._breakpoint = false; + } + } + }, { + key: 'updateSticky', + value: function updateSticky() { + var _this4 = this; + + var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + if (this._running) return; + this._running = true; + + (function (eventType) { + requestAnimationFrame(function () { + switch (eventType) { + // When browser is scrolling and re-calculate just dimensions + // within scroll. + case 'scroll': + _this4._calcDimensionsWithScroll(); + _this4.observeScrollDir(); + _this4.stickyPosition(); + break; + + // When browser is resizing or there's no event, observe width + // breakpoint and re-calculate dimensions. + case 'resize': + default: + _this4._widthBreakpoint(); + _this4.calcDimensions(); + _this4.stickyPosition(true); + break; + } + _this4._running = false; + }); + })(event.type); + } + }, { + key: '_setSupportFeatures', + value: function _setSupportFeatures() { + var support = this.support; + + support.transform = StickySidebar.supportTransform(); + support.transform3d = StickySidebar.supportTransform(true); + } + }, { + key: '_getTranslate', + value: function _getTranslate() { + var y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + + if (this.support.transform3d) return 'translate3d(' + y + ', ' + x + ', ' + z + ')';else if (this.support.translate) return 'translate(' + y + ', ' + x + ')';else return false; + } + }, { + key: 'destroy', + value: function destroy() { + window.removeEventListener('resize', this, { capture: false }); + window.removeEventListener('scroll', this, { capture: false }); + + this.sidebar.classList.remove(this.options.stickyClass); + this.sidebar.style.minHeight = ''; + + this.sidebar.removeEventListener('update' + EVENT_KEY, this); + + var styleReset = { inner: {}, outer: {} }; + + styleReset.inner = { position: '', top: '', left: '', bottom: '', width: '', transform: '' }; + styleReset.outer = { height: '', position: '' }; + + for (var key in styleReset.outer) { + this.sidebar.style[key] = styleReset.outer[key]; + }for (var _key2 in styleReset.inner) { + this.sidebarInner.style[_key2] = styleReset.inner[_key2]; + }if (this.options.resizeSensor && 'undefined' !== typeof ResizeObserver && this.resizeObserver) { + this.resizeObserver.unobserve(this.sidebarInner); + this.resizeObserver.unobserve(this.container); + if (this.scrollContainer) this.resizeObserver.unobserve(this.scrollContainer); + } + } + }], [{ + key: 'supportTransform', + value: function supportTransform(transform3d) { + var result = false, + property = transform3d ? 'perspective' : 'transform', + upper = property.charAt(0).toUpperCase() + property.slice(1), + prefixes = ['Webkit', 'Moz', 'O', 'ms'], + support = document.createElement('support'), + style = support.style; + + (property + ' ' + prefixes.join(upper + ' ') + upper).split(' ').forEach(function (property, i) { + if (style[property] !== undefined) { + result = property; + return false; + } + }); + return result; + } + }, { + key: 'eventTrigger', + value: function eventTrigger(element, eventName, data) { + try { + var event = new CustomEvent(eventName, { detail: data }); + } catch (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent(eventName, true, true, data); + } + element.dispatchEvent(event); + } + }, { + key: 'extend', + value: function extend(defaults, options) { + var results = {}; + for (var key in defaults) { + if ('undefined' !== typeof options[key]) results[key] = options[key];else results[key] = defaults[key]; + } + return results; + } + }, { + key: 'offsetRelative', + value: function offsetRelative(element, scrollContainer) { + var result = { left: 0, top: 0 }; + + do { + var offsetTop = element.offsetTop; + var offsetLeft = element.offsetLeft; + + if (!isNaN(offsetTop)) result.top += offsetTop; + + if (!isNaN(offsetLeft)) result.left += offsetLeft; + + element = 'BODY' === element.tagName ? element.parentElement : element.offsetParent; + } while (element !== scrollContainer && element); + return result; + } + }, { + key: 'addClass', + value: function addClass(element, className) { + if (!StickySidebar.hasClass(element, className)) { + if (element.classList) element.classList.add(className);else element.className += ' ' + className; + } + } + }, { + key: 'removeClass', + value: function removeClass(element, className) { + if (StickySidebar.hasClass(element, className)) { + if (element.classList) element.classList.remove(className);else element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); + } + } + }, { + key: 'hasClass', + value: function hasClass(element, className) { + if (element.classList) return element.classList.contains(className);else return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className); + } + }, { + key: 'defaults', + get: function () { + return DEFAULTS; + } + }]); + + return StickySidebar; + }(); + + return StickySidebar; + }(); + + exports.default = StickySidebar; + + + // Global + // ------------------------- + window.StickySidebar = StickySidebar; + }); + }); + + unwrapExports(stickySidebar); + + var jquery_stickySidebar = createCommonjsModule(function (module, exports) { + (function (global, factory) { + { + factory(stickySidebar); + } + })(commonjsGlobal, function (_stickySidebar) { + + var _stickySidebar2 = _interopRequireDefault(_stickySidebar); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + (function () { + if ('undefined' === typeof window) return; + + var plugin = window.$ || window.jQuery || window.Zepto; + var DATA_NAMESPACE = 'stickySidebar'; + + // Make sure the site has jquery or zepto plugin. + if (plugin) { + var _jQueryPlugin = function (config) { + return this.each(function () { + var $this = plugin(this), + data = plugin(this).data(DATA_NAMESPACE); + + if (!data) { + data = new _stickySidebar2.default(this, typeof config == 'object' && config); + $this.data(DATA_NAMESPACE, data); + } + + if ('string' === typeof config) { + if (data[config] === undefined && ['destroy', 'updateSticky'].indexOf(config) === -1) throw new Error('No method named "' + config + '"'); + + data[config](); + } + }); + }; + + plugin.fn.stickySidebar = _jQueryPlugin; + plugin.fn.stickySidebar.Constructor = _stickySidebar2.default; + + var old = plugin.fn.stickySidebar; + + /** + * Sticky Sidebar No Conflict. + */ + plugin.fn.stickySidebar.noConflict = function () { + plugin.fn.stickySidebar = old; + return this; + }; + } + })(); + }); + }); + + var jquery_stickySidebar$1 = unwrapExports(jquery_stickySidebar); + + return jquery_stickySidebar$1; + +})); //# sourceMappingURL=jquery.sticky-sidebar.js.map diff --git a/dist/jquery.sticky-sidebar.js.map b/dist/jquery.sticky-sidebar.js.map index a90bb21..5c0ee2b 100644 --- a/dist/jquery.sticky-sidebar.js.map +++ b/dist/jquery.sticky-sidebar.js.map @@ -1 +1 @@ -{"version":3,"file":"jquery.sticky-sidebar.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"jquery.sticky-sidebar.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/dist/jquery.sticky-sidebar.min.js b/dist/jquery.sticky-sidebar.min.js index a83e472..2c52cd7 100644 --- a/dist/jquery.sticky-sidebar.min.js +++ b/dist/jquery.sticky-sidebar.min.js @@ -1,8 +1,8 @@ /** * sticky-sidebar - A JavaScript plugin for making smart and high performance. - * @version v3.3.1 + * @version v3.3.5 * @link https://github.com/abouolia/sticky-sidebar * @author Ahmed Bouhuolia * @license The MIT License (MIT) **/ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.StickySidebar=e()}(this,function(){"use strict";"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function t(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function e(t,e){return t(e={exports:{}},e.exports),e.exports}var i=e(function(t,e){(function(t){Object.defineProperty(t,"__esModule",{value:!0});var l,n,e=function(){function n(t,e){for(var i=0;i=t.containerBottom?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):i>=t.containerTop&&(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):e+t.translateY<=n?(t.translateY=n-e,o="VIEWPORT-BOTTOM"):t.containerTop+t.translateY<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getAffixTypeScrollingUp",value:function(){var t=this.dimensions,e=t.sidebarHeight+t.containerTop,i=t.viewportTop+t.topSpacing,n=t.viewportBottom-t.bottomSpacing,o=this.affixedType;return i<=t.translateY+t.containerTop?(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):this.isSidebarFitsViewport()||t.containerTop<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getStyle",value:function(t){if(void 0!==t){var e={inner:{},outer:{}},i=this.dimensions;switch(t){case"VIEWPORT-TOP":e.inner={position:"fixed",top:i.topSpacing,left:i.sidebarLeft-i.viewportLeft,width:i.sidebarWidth};break;case"VIEWPORT-BOTTOM":e.inner={position:"fixed",top:"auto",left:i.sidebarLeft,bottom:i.bottomSpacing,width:i.sidebarWidth};break;case"CONTAINER-BOTTOM":case"VIEWPORT-UNBOTTOM":var n=this._getTranslate(0,i.translateY+"px");e.inner=n?{transform:n}:{position:"absolute",top:i.translateY,width:i.sidebarWidth}}switch(t){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":e.outer={height:i.sidebarHeight,position:"relative"}}return e.outer=c.extend({height:"",position:""},e.outer),e.inner=c.extend({position:"relative",top:"",left:"",bottom:"",width:"",transform:""},e.inner),e}}},{key:"stickyPosition",value:function(t){if(!this._breakpoint){t=this._reStyle||t||!1,this.options.topSpacing,this.options.bottomSpacing;var e=this.getAffixType(),i=this._getStyle(e);if((this.affixedType!=e||t)&&e){var n="affix."+e.toLowerCase().replace("viewport-","")+l;for(var o in c.eventTrigger(this.sidebar,n),"STATIC"===e?c.removeClass(this.sidebar,this.options.stickyClass):c.addClass(this.sidebar,this.options.stickyClass),i.outer){var s="number"==typeof i.outer[o]?"px":"";this.sidebar.style[o]=i.outer[o]+s}for(var r in i.inner){var a="number"==typeof i.inner[r]?"px":"";this.sidebarInner.style[r]=i.inner[r]+a}var p="affixed."+e.toLowerCase().replace("viewport-","")+l;c.eventTrigger(this.sidebar,p)}else this._initialized&&(this.sidebarInner.style.left=i.inner.left);this.affixedType=e}}},{key:"_widthBreakpoint",value:function(){window.innerWidth<=this.options.minWidth?(this._breakpoint=!0,this.affixedType="STATIC",this.sidebar.removeAttribute("style"),c.removeClass(this.sidebar,this.options.stickyClass),this.sidebarInner.removeAttribute("style")):this._breakpoint=!1}},{key:"updateSticky",value:function(){var t,e=this,i=0=t.containerBottom?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):i>=t.containerTop&&(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):e+t.translateY<=n?(t.translateY=n-e,o="VIEWPORT-BOTTOM"):t.containerTop+t.translateY<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getAffixTypeScrollingUp",value:function(){var t=this.dimensions,e=t.sidebarHeight+t.containerTop,i=t.viewportTop+t.topSpacing,n=t.viewportBottom-t.bottomSpacing,o=this.affixedType;return i<=t.translateY+t.containerTop?(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):this.isSidebarFitsViewport()||t.containerTop<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getStyle",value:function(t){if(void 0!==t){var e={inner:{},outer:{}},i=this.dimensions;if(this.options.nativeSticky){switch(t){case"VIEWPORT-TOP":e.inner={position:"sticky",top:i.topSpacing};break;case"VIEWPORT-BOTTOM":e.inner={position:"sticky",top:i.viewportHeight-i.sidebarHeight-i.bottomSpacing};break;case"CONTAINER-BOTTOM":e.inner={position:"sticky",top:"100%"};break;case"VIEWPORT-UNBOTTOM":e.inner={position:"relative",top:i.translateY}}switch(t){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":e.outer={height:"100%",position:"relative"}}}else{switch(t){case"VIEWPORT-TOP":e.inner={position:"fixed",top:i.topSpacing,left:i.sidebarLeft-i.viewportLeft,width:i.sidebarWidth};break;case"VIEWPORT-BOTTOM":e.inner={position:"fixed",top:"auto",left:i.sidebarLeft,bottom:i.bottomSpacing,width:i.sidebarWidth};break;case"CONTAINER-BOTTOM":case"VIEWPORT-UNBOTTOM":var n=this._getTranslate(0,i.translateY+"px");e.inner=n?{transform:n}:{position:"absolute",top:i.translateY,width:i.sidebarWidth}}switch(t){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":e.outer={height:i.sidebarHeight,position:"relative"}}}return e.outer=p.extend({height:"",position:""},e.outer),e.inner=p.extend({position:"relative",top:"",left:"",bottom:"",width:"",transform:""},e.inner),e}}},{key:"stickyPosition",value:function(t){if(!this._breakpoint){t=this._reStyle||t||!1;this.options.topSpacing,this.options.bottomSpacing;var e=this.getAffixType(),i=this._getStyle(e);if((this.affixedType!=e||t)&&e){var n,o,t="affix."+e.toLowerCase().replace("viewport-","")+a;for(n in p.eventTrigger(this.sidebar,t),"STATIC"===e?p.removeClass(this.sidebar,this.options.stickyClass):p.addClass(this.sidebar,this.options.stickyClass),i.outer){var s="number"==typeof i.outer[n]?"px":"";this.sidebar.style[n]=i.outer[n]+s}for(o in i.inner){var r="number"==typeof i.inner[o]?"px":"";this.sidebarInner.style[o]=i.inner[o]+r}t="affixed."+e.toLowerCase().replace("viewport-","")+a;p.eventTrigger(this.sidebar,t)}else this._initialized&&(this.sidebarInner.style.left=i.inner.left);this.affixedType=e}}},{key:"_widthBreakpoint",value:function(){window.innerWidth<=this.options.minWidth?(this._breakpoint=!0,this.affixedType="STATIC",this.sidebar.removeAttribute("style"),p.removeClass(this.sidebar,this.options.stickyClass),this.sidebarInner.removeAttribute("style")):this._breakpoint=!1}},{key:"updateSticky",value:function(){var t,e=this,i=0 - * @license The MIT License (MIT) - */ - var StickySidebar = function () { - - // --------------------------------- - // # Define Constants - // --------------------------------- - // - var EVENT_KEY = '.stickySidebar'; - var DEFAULTS = { - /** - * Additional top spacing of the element when it becomes sticky. - * @type {Numeric|Function} - */ - topSpacing: 0, - - /** - * Additional bottom spacing of the element when it becomes sticky. - * @type {Numeric|Function} - */ - bottomSpacing: 0, - - /** - * Container sidebar selector to know what the beginning and end of sticky element. - * @type {String|False} - */ - containerSelector: false, - - /** - * Inner wrapper selector. - * @type {String} - */ - innerWrapperSelector: '.inner-wrapper-sticky', - - /** - * The name of CSS class to apply to elements when they have become stuck. - * @type {String|False} - */ - stickyClass: 'is-affixed', - - /** - * Detect when sidebar and its container change height so re-calculate their dimensions. - * @type {Boolean} - */ - resizeSensor: true, - - /** - * The sidebar returns to its normal position if its width below this value. - * @type {Numeric} - */ - minWidth: false - }; - - // --------------------------------- - // # Class Definition - // --------------------------------- - // - /** - * Sticky Sidebar Class. - * @public - */ - - var StickySidebar = function () { - - /** - * Sticky Sidebar Constructor. - * @constructor - * @param {HTMLElement|String} sidebar - The sidebar element or sidebar selector. - * @param {Object} options - The options of sticky sidebar. - */ - function StickySidebar(sidebar) { - var _this = this; - - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, StickySidebar); - - this.options = StickySidebar.extend(DEFAULTS, options); - - // Sidebar element query if there's no one, throw error. - this.sidebar = 'string' === typeof sidebar ? document.querySelector(sidebar) : sidebar; - if ('undefined' === typeof this.sidebar) throw new Error("There is no specific sidebar element."); - - this.sidebarInner = false; - this.container = this.sidebar.parentElement; - - // Current Affix Type of sidebar element. - this.affixedType = 'STATIC'; - this.direction = 'down'; - this.support = { - transform: false, - transform3d: false - }; - - this._initialized = false; - this._reStyle = false; - this._breakpoint = false; - - // Dimensions of sidebar, container and screen viewport. - this.dimensions = { - translateY: 0, - maxTranslateY: 0, - topSpacing: 0, - lastTopSpacing: 0, - bottomSpacing: 0, - lastBottomSpacing: 0, - sidebarHeight: 0, - sidebarWidth: 0, - containerTop: 0, - containerHeight: 0, - viewportHeight: 0, - viewportTop: 0, - lastViewportTop: 0 - }; - - // Bind event handlers for referencability. - ['handleEvent'].forEach(function (method) { - _this[method] = _this[method].bind(_this); - }); - - // Initialize sticky sidebar for first time. - this.initialize(); - } - - /** - * Initializes the sticky sidebar by adding inner wrapper, define its container, - * min-width breakpoint, calculating dimensions, adding helper classes and inline style. - * @private - */ - - - _createClass(StickySidebar, [{ - key: 'initialize', - value: function initialize() { - var _this2 = this; - - this._setSupportFeatures(); - - // Get sticky sidebar inner wrapper, if not found, will create one. - if (this.options.innerWrapperSelector) { - this.sidebarInner = this.sidebar.querySelector(this.options.innerWrapperSelector); - - if (null === this.sidebarInner) this.sidebarInner = false; - } - - if (!this.sidebarInner) { - var wrapper = document.createElement('div'); - wrapper.setAttribute('class', 'inner-wrapper-sticky'); - this.sidebar.appendChild(wrapper); - - while (this.sidebar.firstChild != wrapper) { - wrapper.appendChild(this.sidebar.firstChild); - }this.sidebarInner = this.sidebar.querySelector('.inner-wrapper-sticky'); - } - - // Container wrapper of the sidebar. - if (this.options.containerSelector) { - var containers = document.querySelectorAll(this.options.containerSelector); - containers = Array.prototype.slice.call(containers); - - containers.forEach(function (container, item) { - if (!container.contains(_this2.sidebar)) return; - _this2.container = container; - }); - - if (!containers.length) throw new Error("The container does not contains on the sidebar."); - } - - // If top/bottom spacing is not function parse value to integer. - if ('function' !== typeof this.options.topSpacing) this.options.topSpacing = parseInt(this.options.topSpacing) || 0; - - if ('function' !== typeof this.options.bottomSpacing) this.options.bottomSpacing = parseInt(this.options.bottomSpacing) || 0; - - // Breakdown sticky sidebar if screen width below `options.minWidth`. - this._widthBreakpoint(); - - // Calculate dimensions of sidebar, container and viewport. - this.calcDimensions(); - - // Affix sidebar in proper position. - this.stickyPosition(); - - // Bind all events. - this.bindEvents(); - - // Inform other properties the sticky sidebar is initialized. - this._initialized = true; - } - }, { - key: 'bindEvents', - value: function bindEvents() { - window.addEventListener('resize', this, { passive: true, capture: false }); - window.addEventListener('scroll', this, { passive: true, capture: false }); - - this.sidebar.addEventListener('update' + EVENT_KEY, this); - - if (this.options.resizeSensor && 'undefined' !== typeof ResizeSensor) { - new ResizeSensor(this.sidebarInner, this.handleEvent); - new ResizeSensor(this.container, this.handleEvent); - } - } - }, { - key: 'handleEvent', - value: function handleEvent(event) { - this.updateSticky(event); - } - }, { - key: 'calcDimensions', - value: function calcDimensions() { - if (this._breakpoint) return; - var dims = this.dimensions; - - // Container of sticky sidebar dimensions. - dims.containerTop = StickySidebar.offsetRelative(this.container).top; - dims.containerHeight = this.container.clientHeight; - dims.containerBottom = dims.containerTop + dims.containerHeight; - - // Sidebar dimensions. - dims.sidebarHeight = this.sidebarInner.offsetHeight; - dims.sidebarWidth = this.sidebarInner.offsetWidth; - - // Screen viewport dimensions. - dims.viewportHeight = window.innerHeight; - - // Maximum sidebar translate Y. - dims.maxTranslateY = dims.containerHeight - dims.sidebarHeight; - - this._calcDimensionsWithScroll(); - } - }, { - key: '_calcDimensionsWithScroll', - value: function _calcDimensionsWithScroll() { - var dims = this.dimensions; - - dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar).left; - - dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop; - dims.viewportBottom = dims.viewportTop + dims.viewportHeight; - dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft; - - dims.topSpacing = this.options.topSpacing; - dims.bottomSpacing = this.options.bottomSpacing; - - if ('function' === typeof dims.topSpacing) dims.topSpacing = parseInt(dims.topSpacing(this.sidebar)) || 0; - - if ('function' === typeof dims.bottomSpacing) dims.bottomSpacing = parseInt(dims.bottomSpacing(this.sidebar)) || 0; - - if ('VIEWPORT-TOP' === this.affixedType) { - // Adjust translate Y in the case decrease top spacing value. - if (dims.topSpacing < dims.lastTopSpacing) { - dims.translateY += dims.lastTopSpacing - dims.topSpacing; - this._reStyle = true; - } - } else if ('VIEWPORT-BOTTOM' === this.affixedType) { - // Adjust translate Y in the case decrease bottom spacing value. - if (dims.bottomSpacing < dims.lastBottomSpacing) { - dims.translateY += dims.lastBottomSpacing - dims.bottomSpacing; - this._reStyle = true; - } - } - - dims.lastTopSpacing = dims.topSpacing; - dims.lastBottomSpacing = dims.bottomSpacing; - } - }, { - key: 'isSidebarFitsViewport', - value: function isSidebarFitsViewport() { - var dims = this.dimensions; - var offset = this.scrollDirection === 'down' ? dims.lastBottomSpacing : dims.lastTopSpacing; - return this.dimensions.sidebarHeight + offset < this.dimensions.viewportHeight; - } - }, { - key: 'observeScrollDir', - value: function observeScrollDir() { - var dims = this.dimensions; - if (dims.lastViewportTop === dims.viewportTop) return; - - var furthest = 'down' === this.direction ? Math.min : Math.max; - - // If the browser is scrolling not in the same direction. - if (dims.viewportTop === furthest(dims.viewportTop, dims.lastViewportTop)) this.direction = 'down' === this.direction ? 'up' : 'down'; - } - }, { - key: 'getAffixType', - value: function getAffixType() { - this._calcDimensionsWithScroll(); - var dims = this.dimensions; - var colliderTop = dims.viewportTop + dims.topSpacing; - var affixType = this.affixedType; - - if (colliderTop <= dims.containerTop || dims.containerHeight <= dims.sidebarHeight) { - dims.translateY = 0; - affixType = 'STATIC'; - } else { - affixType = 'up' === this.direction ? this._getAffixTypeScrollingUp() : this._getAffixTypeScrollingDown(); - } - - // Make sure the translate Y is not bigger than container height. - dims.translateY = Math.max(0, dims.translateY); - dims.translateY = Math.min(dims.containerHeight, dims.translateY); - dims.translateY = Math.round(dims.translateY); - - dims.lastViewportTop = dims.viewportTop; - return affixType; - } - }, { - key: '_getAffixTypeScrollingDown', - value: function _getAffixTypeScrollingDown() { - var dims = this.dimensions; - var sidebarBottom = dims.sidebarHeight + dims.containerTop; - var colliderTop = dims.viewportTop + dims.topSpacing; - var colliderBottom = dims.viewportBottom - dims.bottomSpacing; - var affixType = this.affixedType; - - if (this.isSidebarFitsViewport()) { - if (dims.sidebarHeight + colliderTop >= dims.containerBottom) { - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; - } else if (colliderTop >= dims.containerTop) { - dims.translateY = colliderTop - dims.containerTop; - affixType = 'VIEWPORT-TOP'; - } - } else { - if (dims.containerBottom <= colliderBottom) { - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; - } else if (sidebarBottom + dims.translateY <= colliderBottom) { - dims.translateY = colliderBottom - sidebarBottom; - affixType = 'VIEWPORT-BOTTOM'; - } else if (dims.containerTop + dims.translateY <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { - affixType = 'VIEWPORT-UNBOTTOM'; - } - } - - return affixType; - } - }, { - key: '_getAffixTypeScrollingUp', - value: function _getAffixTypeScrollingUp() { - var dims = this.dimensions; - var sidebarBottom = dims.sidebarHeight + dims.containerTop; - var colliderTop = dims.viewportTop + dims.topSpacing; - var colliderBottom = dims.viewportBottom - dims.bottomSpacing; - var affixType = this.affixedType; - - if (colliderTop <= dims.translateY + dims.containerTop) { - dims.translateY = colliderTop - dims.containerTop; - affixType = 'VIEWPORT-TOP'; - } else if (dims.containerBottom <= colliderBottom) { - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; - } else if (!this.isSidebarFitsViewport()) { - - if (dims.containerTop <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { - affixType = 'VIEWPORT-UNBOTTOM'; - } - } - - return affixType; - } - }, { - key: '_getStyle', - value: function _getStyle(affixType) { - if ('undefined' === typeof affixType) return; - - var style = { inner: {}, outer: {} }; - var dims = this.dimensions; - - switch (affixType) { - case 'VIEWPORT-TOP': - style.inner = { position: 'fixed', top: dims.topSpacing, - left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth }; - break; - case 'VIEWPORT-BOTTOM': - style.inner = { position: 'fixed', top: 'auto', left: dims.sidebarLeft, - bottom: dims.bottomSpacing, width: dims.sidebarWidth }; - break; - case 'CONTAINER-BOTTOM': - case 'VIEWPORT-UNBOTTOM': - var translate = this._getTranslate(0, dims.translateY + 'px'); - - if (translate) style.inner = { transform: translate };else style.inner = { position: 'absolute', top: dims.translateY, width: dims.sidebarWidth }; - break; - } - - switch (affixType) { - case 'VIEWPORT-TOP': - case 'VIEWPORT-BOTTOM': - case 'VIEWPORT-UNBOTTOM': - case 'CONTAINER-BOTTOM': - style.outer = { height: dims.sidebarHeight, position: 'relative' }; - break; - } - - style.outer = StickySidebar.extend({ height: '', position: '' }, style.outer); - style.inner = StickySidebar.extend({ position: 'relative', top: '', left: '', - bottom: '', width: '', transform: '' }, style.inner); - - return style; - } - }, { - key: 'stickyPosition', - value: function stickyPosition(force) { - if (this._breakpoint) return; - - force = this._reStyle || force || false; - - var offsetTop = this.options.topSpacing; - var offsetBottom = this.options.bottomSpacing; - - var affixType = this.getAffixType(); - var style = this._getStyle(affixType); - - if ((this.affixedType != affixType || force) && affixType) { - var affixEvent = 'affix.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; - StickySidebar.eventTrigger(this.sidebar, affixEvent); - - if ('STATIC' === affixType) StickySidebar.removeClass(this.sidebar, this.options.stickyClass);else StickySidebar.addClass(this.sidebar, this.options.stickyClass); - - for (var key in style.outer) { - var unit = 'number' === typeof style.outer[key] ? 'px' : ''; - this.sidebar.style[key] = style.outer[key] + unit; - } - - for (var _key in style.inner) { - var _unit = 'number' === typeof style.inner[_key] ? 'px' : ''; - this.sidebarInner.style[_key] = style.inner[_key] + _unit; - } - - var affixedEvent = 'affixed.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; - StickySidebar.eventTrigger(this.sidebar, affixedEvent); - } else { - if (this._initialized) this.sidebarInner.style.left = style.inner.left; - } - - this.affixedType = affixType; - } - }, { - key: '_widthBreakpoint', - value: function _widthBreakpoint() { - - if (window.innerWidth <= this.options.minWidth) { - this._breakpoint = true; - this.affixedType = 'STATIC'; - - this.sidebar.removeAttribute('style'); - StickySidebar.removeClass(this.sidebar, this.options.stickyClass); - this.sidebarInner.removeAttribute('style'); - } else { - this._breakpoint = false; - } - } - }, { - key: 'updateSticky', - value: function updateSticky() { - var _this3 = this; - - var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - - if (this._running) return; - this._running = true; - - (function (eventType) { - requestAnimationFrame(function () { - switch (eventType) { - // When browser is scrolling and re-calculate just dimensions - // within scroll. - case 'scroll': - _this3._calcDimensionsWithScroll(); - _this3.observeScrollDir(); - _this3.stickyPosition(); - break; - - // When browser is resizing or there's no event, observe width - // breakpoint and re-calculate dimensions. - case 'resize': - default: - _this3._widthBreakpoint(); - _this3.calcDimensions(); - _this3.stickyPosition(true); - break; - } - _this3._running = false; - }); - })(event.type); - } - }, { - key: '_setSupportFeatures', - value: function _setSupportFeatures() { - var support = this.support; - - support.transform = StickySidebar.supportTransform(); - support.transform3d = StickySidebar.supportTransform(true); - } - }, { - key: '_getTranslate', - value: function _getTranslate() { - var y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - - if (this.support.transform3d) return 'translate3d(' + y + ', ' + x + ', ' + z + ')';else if (this.support.translate) return 'translate(' + y + ', ' + x + ')';else return false; - } - }, { - key: 'destroy', - value: function destroy() { - window.removeEventListener('resize', this, { capture: false }); - window.removeEventListener('scroll', this, { capture: false }); - - this.sidebar.classList.remove(this.options.stickyClass); - this.sidebar.style.minHeight = ''; - - this.sidebar.removeEventListener('update' + EVENT_KEY, this); - - var styleReset = { inner: {}, outer: {} }; - - styleReset.inner = { position: '', top: '', left: '', bottom: '', width: '', transform: '' }; - styleReset.outer = { height: '', position: '' }; - - for (var key in styleReset.outer) { - this.sidebar.style[key] = styleReset.outer[key]; - }for (var _key2 in styleReset.inner) { - this.sidebarInner.style[_key2] = styleReset.inner[_key2]; - }if (this.options.resizeSensor && 'undefined' !== typeof ResizeSensor) { - ResizeSensor.detach(this.sidebarInner, this.handleEvent); - ResizeSensor.detach(this.container, this.handleEvent); - } - } - }], [{ - key: 'supportTransform', - value: function supportTransform(transform3d) { - var result = false, - property = transform3d ? 'perspective' : 'transform', - upper = property.charAt(0).toUpperCase() + property.slice(1), - prefixes = ['Webkit', 'Moz', 'O', 'ms'], - support = document.createElement('support'), - style = support.style; - - (property + ' ' + prefixes.join(upper + ' ') + upper).split(' ').forEach(function (property, i) { - if (style[property] !== undefined) { - result = property; - return false; - } - }); - return result; - } - }, { - key: 'eventTrigger', - value: function eventTrigger(element, eventName, data) { - try { - var event = new CustomEvent(eventName, { detail: data }); - } catch (e) { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent(eventName, true, true, data); - } - element.dispatchEvent(event); - } - }, { - key: 'extend', - value: function extend(defaults, options) { - var results = {}; - for (var key in defaults) { - if ('undefined' !== typeof options[key]) results[key] = options[key];else results[key] = defaults[key]; - } - return results; - } - }, { - key: 'offsetRelative', - value: function offsetRelative(element) { - var result = { left: 0, top: 0 }; - - do { - var offsetTop = element.offsetTop; - var offsetLeft = element.offsetLeft; - - if (!isNaN(offsetTop)) result.top += offsetTop; - - if (!isNaN(offsetLeft)) result.left += offsetLeft; - - element = 'BODY' === element.tagName ? element.parentElement : element.offsetParent; - } while (element); - return result; - } - }, { - key: 'addClass', - value: function addClass(element, className) { - if (!StickySidebar.hasClass(element, className)) { - if (element.classList) element.classList.add(className);else element.className += ' ' + className; - } - } - }, { - key: 'removeClass', - value: function removeClass(element, className) { - if (StickySidebar.hasClass(element, className)) { - if (element.classList) element.classList.remove(className);else element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); - } - } - }, { - key: 'hasClass', - value: function hasClass(element, className) { - if (element.classList) return element.classList.contains(className);else return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className); - } - }, { - key: 'defaults', - get: function () { - return DEFAULTS; - } - }]); - - return StickySidebar; - }(); - - return StickySidebar; - }(); - - exports.default = StickySidebar; - - - // Global - // ------------------------- - window.StickySidebar = StickySidebar; -}); -}); - -var stickySidebar$1 = unwrapExports(stickySidebar); - -exports['default'] = stickySidebar$1; -exports.__moduleExports = stickySidebar; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); + (global = global || self, factory(global.StickySidebar = {})); +}(this, function (exports) { 'use strict'; + + var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function unwrapExports (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var stickySidebar = createCommonjsModule(function (module, exports) { + (function (global, factory) { + { + factory(exports); + } + })(commonjsGlobal, function (exports) { + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + /** + * Sticky Sidebar JavaScript Plugin. + * @version 3.3.5 + * @author Ahmed Bouhuolia + * @license The MIT License (MIT) + */ + var StickySidebar = function () { + + // --------------------------------- + // # Define Constants + // --------------------------------- + // + var EVENT_KEY = '.stickySidebar'; + + var DEFAULTS = { + /** + * Additional top spacing of the element when it becomes sticky. + * @type {Numeric|Function} + */ + topSpacing: 0, + + /** + * Additional bottom spacing of the element when it becomes sticky. + * @type {Numeric|Function} + */ + bottomSpacing: 0, + + /** + * Scroll container selector. + * @type {String|False} + */ + scrollContainerSelector: false, + + /** + * Container sidebar selector to know what the beginning and end of sticky element. + * @type {String|False} + */ + containerSelector: false, + + /** + * Inner wrapper selector. + * @type {String} + */ + innerWrapperSelector: '.inner-wrapper-sticky', + + /** + * The name of CSS class to apply to elements when they have become stuck. + * @type {String|False} + */ + stickyClass: 'is-affixed', + + /** + * Detect when sidebar and its container change height so re-calculate their dimensions. + * @type {Boolean} + */ + resizeSensor: true, + + /** + * The sidebar returns to its normal position if its width below this value. + * @type {Numeric} + */ + minWidth: false, + + /** + * Use native CSS position sticky. + * @type {Boolean} + */ + nativeSticky: false + }; + + // --------------------------------- + // # Class Definition + // --------------------------------- + // + /** + * Sticky Sidebar Class. + * @public + */ + + var StickySidebar = function () { + + /** + * Sticky Sidebar Constructor. + * @constructor + * @param {HTMLElement|String} sidebar - The sidebar element or sidebar selector. + * @param {Object} options - The options of sticky sidebar. + */ + function StickySidebar(sidebar) { + var _this = this; + + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, StickySidebar); + + this.options = StickySidebar.extend(DEFAULTS, options); + + // Sidebar element query if there's no one, throw error. + this.sidebar = 'string' === typeof sidebar ? document.querySelector(sidebar) : sidebar; + if ('undefined' === typeof this.sidebar) throw new Error("There is no specific sidebar element."); + + this.sidebarInner = false; + this.container = this.sidebar.parentElement; + + // Get scroll container, if provided + this.scrollContainer = this.options.scrollContainerSelector ? 'string' === typeof this.options.scrollContainerSelector ? document.querySelector(this.options.scrollContainerSelector) : this.options.scrollContainerSelector : undefined; + + // Current Affix Type of sidebar element. + this.affixedType = 'STATIC'; + this.direction = 'down'; + this.support = { + transform: false, + transform3d: false + }; + + this._initialized = false; + this._reStyle = false; + this._breakpoint = false; + + // Dimensions of sidebar, container and screen viewport. + this.dimensions = { + translateY: 0, + maxTranslateY: 0, + topSpacing: 0, + lastTopSpacing: 0, + bottomSpacing: 0, + lastBottomSpacing: 0, + sidebarHeight: 0, + sidebarWidth: 0, + containerTop: 0, + containerHeight: 0, + viewportHeight: 0, + viewportTop: 0, + lastViewportTop: 0 + }; + + // Bind event handlers for referencability. + ['handleEvent'].forEach(function (method) { + _this[method] = _this[method].bind(_this); + }); + + // Initialize sticky sidebar for first time. + this.initialize(); + } + + /** + * Initializes the sticky sidebar by adding inner wrapper, define its container, + * min-width breakpoint, calculating dimensions, adding helper classes and inline style. + * @private + */ + + + _createClass(StickySidebar, [{ + key: 'initialize', + value: function initialize() { + var _this2 = this; + + this._setSupportFeatures(); + + // Get sticky sidebar inner wrapper, if not found, will create one. + if (this.options.innerWrapperSelector) { + this.sidebarInner = this.sidebar.querySelector(this.options.innerWrapperSelector); + + if (null === this.sidebarInner) this.sidebarInner = false; + } + + if (!this.sidebarInner) { + var wrapper = document.createElement('div'); + wrapper.setAttribute('class', 'inner-wrapper-sticky'); + this.sidebar.appendChild(wrapper); + + while (this.sidebar.firstChild != wrapper) { + wrapper.appendChild(this.sidebar.firstChild); + }this.sidebarInner = this.sidebar.querySelector('.inner-wrapper-sticky'); + } + + // Container wrapper of the sidebar. + if (this.options.containerSelector) { + var containers = document.querySelectorAll(this.options.containerSelector); + containers = Array.prototype.slice.call(containers); + + containers.forEach(function (container, item) { + if (!container.contains(_this2.sidebar)) return; + _this2.container = container; + }); + + if (!containers.length) throw new Error("The container does not contains on the sidebar."); + } + + // If top/bottom spacing is not function parse value to integer. + if ('function' !== typeof this.options.topSpacing) this.options.topSpacing = parseInt(this.options.topSpacing) || 0; + + if ('function' !== typeof this.options.bottomSpacing) this.options.bottomSpacing = parseInt(this.options.bottomSpacing) || 0; + + // Breakdown sticky sidebar if screen width below `options.minWidth`. + this._widthBreakpoint(); + + // Calculate dimensions of sidebar, container and viewport. + this.calcDimensions(); + + // Affix sidebar in proper position. + this.stickyPosition(); + + // Bind all events. + this.bindEvents(); + + // Inform other properties the sticky sidebar is initialized. + this._initialized = true; + } + }, { + key: 'bindEvents', + value: function bindEvents() { + var _this3 = this; + + window.addEventListener('resize', this, { passive: true, capture: false }); + (this.scrollContainer || window).addEventListener('scroll', this, { passive: true, capture: false }); + + this.sidebar.addEventListener('update' + EVENT_KEY, this); + + if (this.options.resizeSensor && 'undefined' !== typeof ResizeObserver) { + this.resizeObserver = new ResizeObserver(function () { + return _this3.handleEvent({ type: 'resize' }); + }); + this.resizeObserver.observe(this.sidebarInner); + this.resizeObserver.observe(this.container); + if (this.scrollContainer) this.resizeObserver.observe(this.scrollContainer); + } + } + }, { + key: 'handleEvent', + value: function handleEvent(event) { + this.updateSticky(event); + } + }, { + key: 'calcDimensions', + value: function calcDimensions() { + if (this._breakpoint) return; + var dims = this.dimensions; + + // Container of sticky sidebar dimensions. + dims.containerTop = StickySidebar.offsetRelative(this.container, this.scrollContainer).top; + dims.containerHeight = this.container.clientHeight; + dims.containerBottom = dims.containerTop + dims.containerHeight; + + // Sidebar dimensions. + dims.sidebarHeight = this.sidebarInner.offsetHeight; + dims.sidebarWidth = this.sidebarInner.offsetWidth; + + // Screen viewport dimensions. + if (this.scrollContainer) { + var scrollContainerPaddingTop = parseFloat(getComputedStyle(this.scrollContainer).paddingTop) || 0; + var scrollContainerPaddingBottom = parseFloat(getComputedStyle(this.scrollContainer).paddingBottom) || 0; + + dims.viewportHeight = this.scrollContainer.clientHeight - scrollContainerPaddingTop - scrollContainerPaddingBottom; + } else { + dims.viewportHeight = window.innerHeight; + } + + // Maximum sidebar translate Y. + dims.maxTranslateY = dims.containerHeight - dims.sidebarHeight; + + this._calcDimensionsWithScroll(); + } + }, { + key: '_calcDimensionsWithScroll', + value: function _calcDimensionsWithScroll() { + var dims = this.dimensions; + + dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar, this.scrollContainer).left; + + if (this.scrollContainer) { + var scrollContainerPaddingTop = parseFloat(getComputedStyle(this.scrollContainer).paddingTop) || 0; + var scrollContainerPaddingLeft = parseFloat(getComputedStyle(this.scrollContainer).paddingLeft) || 0; + + dims.viewportTop = this.scrollContainer.scrollTop + scrollContainerPaddingTop; + dims.viewportLeft = this.scrollContainer.scrollLeft + scrollContainerPaddingLeft; + } else { + dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop; + dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + } + dims.viewportBottom = dims.viewportTop + dims.viewportHeight; + + dims.topSpacing = this.options.topSpacing; + dims.bottomSpacing = this.options.bottomSpacing; + + if ('function' === typeof dims.topSpacing) dims.topSpacing = parseInt(dims.topSpacing(this.sidebar)) || 0; + + if ('function' === typeof dims.bottomSpacing) dims.bottomSpacing = parseInt(dims.bottomSpacing(this.sidebar)) || 0; + + if ('VIEWPORT-TOP' === this.affixedType) { + // Adjust translate Y in the case decrease top spacing value. + if (dims.topSpacing < dims.lastTopSpacing) { + dims.translateY += dims.lastTopSpacing - dims.topSpacing; + this._reStyle = true; + } + } else if ('VIEWPORT-BOTTOM' === this.affixedType) { + // Adjust translate Y in the case decrease bottom spacing value. + if (dims.bottomSpacing < dims.lastBottomSpacing) { + dims.translateY += dims.lastBottomSpacing - dims.bottomSpacing; + this._reStyle = true; + } + } + + dims.lastTopSpacing = dims.topSpacing; + dims.lastBottomSpacing = dims.bottomSpacing; + } + }, { + key: 'isSidebarFitsViewport', + value: function isSidebarFitsViewport() { + var dims = this.dimensions; + var offset = this.scrollDirection === 'down' ? dims.lastBottomSpacing : dims.lastTopSpacing; + return this.dimensions.sidebarHeight + offset < this.dimensions.viewportHeight; + } + }, { + key: 'observeScrollDir', + value: function observeScrollDir() { + var dims = this.dimensions; + if (dims.lastViewportTop === dims.viewportTop) return; + + var furthest = 'down' === this.direction ? Math.min : Math.max; + + // If the browser is scrolling not in the same direction. + if (dims.viewportTop === furthest(dims.viewportTop, dims.lastViewportTop)) this.direction = 'down' === this.direction ? 'up' : 'down'; + } + }, { + key: 'getAffixType', + value: function getAffixType() { + this._calcDimensionsWithScroll(); + var dims = this.dimensions; + var colliderTop = dims.viewportTop + dims.topSpacing; + var affixType = this.affixedType; + + if (colliderTop <= dims.containerTop || dims.containerHeight <= dims.sidebarHeight) { + dims.translateY = 0; + affixType = 'STATIC'; + } else { + affixType = 'up' === this.direction ? this._getAffixTypeScrollingUp() : this._getAffixTypeScrollingDown(); + } + + // Make sure the translate Y is not bigger than container height. + dims.translateY = Math.max(0, dims.translateY); + dims.translateY = Math.min(dims.containerHeight, dims.translateY); + dims.translateY = Math.round(dims.translateY); + + dims.lastViewportTop = dims.viewportTop; + return affixType; + } + }, { + key: '_getAffixTypeScrollingDown', + value: function _getAffixTypeScrollingDown() { + var dims = this.dimensions; + var sidebarBottom = dims.sidebarHeight + dims.containerTop; + var colliderTop = dims.viewportTop + dims.topSpacing; + var colliderBottom = dims.viewportBottom - dims.bottomSpacing; + var affixType = this.affixedType; + + if (this.isSidebarFitsViewport()) { + if (dims.sidebarHeight + colliderTop >= dims.containerBottom) { + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; + } else if (colliderTop >= dims.containerTop) { + dims.translateY = colliderTop - dims.containerTop; + affixType = 'VIEWPORT-TOP'; + } + } else { + if (dims.containerBottom <= colliderBottom) { + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; + } else if (sidebarBottom + dims.translateY <= colliderBottom) { + dims.translateY = colliderBottom - sidebarBottom; + affixType = 'VIEWPORT-BOTTOM'; + } else if (dims.containerTop + dims.translateY <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { + affixType = 'VIEWPORT-UNBOTTOM'; + } + } + + return affixType; + } + }, { + key: '_getAffixTypeScrollingUp', + value: function _getAffixTypeScrollingUp() { + var dims = this.dimensions; + var sidebarBottom = dims.sidebarHeight + dims.containerTop; + var colliderTop = dims.viewportTop + dims.topSpacing; + var colliderBottom = dims.viewportBottom - dims.bottomSpacing; + var affixType = this.affixedType; + + if (colliderTop <= dims.translateY + dims.containerTop) { + dims.translateY = colliderTop - dims.containerTop; + affixType = 'VIEWPORT-TOP'; + } else if (dims.containerBottom <= colliderBottom) { + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; + } else if (!this.isSidebarFitsViewport()) { + + if (dims.containerTop <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) { + affixType = 'VIEWPORT-UNBOTTOM'; + } + } + + return affixType; + } + }, { + key: '_getStyle', + value: function _getStyle(affixType) { + if ('undefined' === typeof affixType) return; + + var style = { inner: {}, outer: {} }; + var dims = this.dimensions; + + if (this.options.nativeSticky) { + switch (affixType) { + case 'VIEWPORT-TOP': + style.inner = { position: 'sticky', top: dims.topSpacing }; + break; + case 'VIEWPORT-BOTTOM': + style.inner = { position: 'sticky', top: dims.viewportHeight - dims.sidebarHeight - dims.bottomSpacing }; + break; + case 'CONTAINER-BOTTOM': + style.inner = { position: 'sticky', top: '100%' }; + break; + case 'VIEWPORT-UNBOTTOM': + style.inner = { position: 'relative', top: dims.translateY }; + break; + } + + switch (affixType) { + case 'VIEWPORT-TOP': + case 'VIEWPORT-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + case 'CONTAINER-BOTTOM': + style.outer = { height: '100%', position: 'relative' }; + break; + } + } else { + switch (affixType) { + case 'VIEWPORT-TOP': + style.inner = { position: 'fixed', top: dims.topSpacing, + left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth }; + break; + case 'VIEWPORT-BOTTOM': + style.inner = { position: 'fixed', top: 'auto', left: dims.sidebarLeft, + bottom: dims.bottomSpacing, width: dims.sidebarWidth }; + break; + case 'CONTAINER-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + var translate = this._getTranslate(0, dims.translateY + 'px'); + + if (translate) style.inner = { transform: translate };else style.inner = { position: 'absolute', top: dims.translateY, width: dims.sidebarWidth }; + break; + } + + switch (affixType) { + case 'VIEWPORT-TOP': + case 'VIEWPORT-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + case 'CONTAINER-BOTTOM': + style.outer = { height: dims.sidebarHeight, position: 'relative' }; + break; + } + } + + style.outer = StickySidebar.extend({ height: '', position: '' }, style.outer); + style.inner = StickySidebar.extend({ position: 'relative', top: '', left: '', + bottom: '', width: '', transform: '' }, style.inner); + + return style; + } + }, { + key: 'stickyPosition', + value: function stickyPosition(force) { + if (this._breakpoint) return; + + force = this._reStyle || force || false; + + var offsetTop = this.options.topSpacing; + var offsetBottom = this.options.bottomSpacing; + + var affixType = this.getAffixType(); + var style = this._getStyle(affixType); + + if ((this.affixedType != affixType || force) && affixType) { + var affixEvent = 'affix.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; + StickySidebar.eventTrigger(this.sidebar, affixEvent); + + if ('STATIC' === affixType) StickySidebar.removeClass(this.sidebar, this.options.stickyClass);else StickySidebar.addClass(this.sidebar, this.options.stickyClass); + + for (var key in style.outer) { + var unit = 'number' === typeof style.outer[key] ? 'px' : ''; + this.sidebar.style[key] = style.outer[key] + unit; + } + + for (var _key in style.inner) { + var _unit = 'number' === typeof style.inner[_key] ? 'px' : ''; + this.sidebarInner.style[_key] = style.inner[_key] + _unit; + } + + var affixedEvent = 'affixed.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; + StickySidebar.eventTrigger(this.sidebar, affixedEvent); + } else { + if (this._initialized) this.sidebarInner.style.left = style.inner.left; + } + + this.affixedType = affixType; + } + }, { + key: '_widthBreakpoint', + value: function _widthBreakpoint() { + + if (window.innerWidth <= this.options.minWidth) { + this._breakpoint = true; + this.affixedType = 'STATIC'; + + this.sidebar.removeAttribute('style'); + StickySidebar.removeClass(this.sidebar, this.options.stickyClass); + this.sidebarInner.removeAttribute('style'); + } else { + this._breakpoint = false; + } + } + }, { + key: 'updateSticky', + value: function updateSticky() { + var _this4 = this; + + var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + if (this._running) return; + this._running = true; + + (function (eventType) { + requestAnimationFrame(function () { + switch (eventType) { + // When browser is scrolling and re-calculate just dimensions + // within scroll. + case 'scroll': + _this4._calcDimensionsWithScroll(); + _this4.observeScrollDir(); + _this4.stickyPosition(); + break; + + // When browser is resizing or there's no event, observe width + // breakpoint and re-calculate dimensions. + case 'resize': + default: + _this4._widthBreakpoint(); + _this4.calcDimensions(); + _this4.stickyPosition(true); + break; + } + _this4._running = false; + }); + })(event.type); + } + }, { + key: '_setSupportFeatures', + value: function _setSupportFeatures() { + var support = this.support; + + support.transform = StickySidebar.supportTransform(); + support.transform3d = StickySidebar.supportTransform(true); + } + }, { + key: '_getTranslate', + value: function _getTranslate() { + var y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + + if (this.support.transform3d) return 'translate3d(' + y + ', ' + x + ', ' + z + ')';else if (this.support.translate) return 'translate(' + y + ', ' + x + ')';else return false; + } + }, { + key: 'destroy', + value: function destroy() { + window.removeEventListener('resize', this, { capture: false }); + window.removeEventListener('scroll', this, { capture: false }); + + this.sidebar.classList.remove(this.options.stickyClass); + this.sidebar.style.minHeight = ''; + + this.sidebar.removeEventListener('update' + EVENT_KEY, this); + + var styleReset = { inner: {}, outer: {} }; + + styleReset.inner = { position: '', top: '', left: '', bottom: '', width: '', transform: '' }; + styleReset.outer = { height: '', position: '' }; + + for (var key in styleReset.outer) { + this.sidebar.style[key] = styleReset.outer[key]; + }for (var _key2 in styleReset.inner) { + this.sidebarInner.style[_key2] = styleReset.inner[_key2]; + }if (this.options.resizeSensor && 'undefined' !== typeof ResizeObserver && this.resizeObserver) { + this.resizeObserver.unobserve(this.sidebarInner); + this.resizeObserver.unobserve(this.container); + if (this.scrollContainer) this.resizeObserver.unobserve(this.scrollContainer); + } + } + }], [{ + key: 'supportTransform', + value: function supportTransform(transform3d) { + var result = false, + property = transform3d ? 'perspective' : 'transform', + upper = property.charAt(0).toUpperCase() + property.slice(1), + prefixes = ['Webkit', 'Moz', 'O', 'ms'], + support = document.createElement('support'), + style = support.style; + + (property + ' ' + prefixes.join(upper + ' ') + upper).split(' ').forEach(function (property, i) { + if (style[property] !== undefined) { + result = property; + return false; + } + }); + return result; + } + }, { + key: 'eventTrigger', + value: function eventTrigger(element, eventName, data) { + try { + var event = new CustomEvent(eventName, { detail: data }); + } catch (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent(eventName, true, true, data); + } + element.dispatchEvent(event); + } + }, { + key: 'extend', + value: function extend(defaults, options) { + var results = {}; + for (var key in defaults) { + if ('undefined' !== typeof options[key]) results[key] = options[key];else results[key] = defaults[key]; + } + return results; + } + }, { + key: 'offsetRelative', + value: function offsetRelative(element, scrollContainer) { + var result = { left: 0, top: 0 }; + + do { + var offsetTop = element.offsetTop; + var offsetLeft = element.offsetLeft; + + if (!isNaN(offsetTop)) result.top += offsetTop; + + if (!isNaN(offsetLeft)) result.left += offsetLeft; + + element = 'BODY' === element.tagName ? element.parentElement : element.offsetParent; + } while (element !== scrollContainer && element); + return result; + } + }, { + key: 'addClass', + value: function addClass(element, className) { + if (!StickySidebar.hasClass(element, className)) { + if (element.classList) element.classList.add(className);else element.className += ' ' + className; + } + } + }, { + key: 'removeClass', + value: function removeClass(element, className) { + if (StickySidebar.hasClass(element, className)) { + if (element.classList) element.classList.remove(className);else element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); + } + } + }, { + key: 'hasClass', + value: function hasClass(element, className) { + if (element.classList) return element.classList.contains(className);else return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className); + } + }, { + key: 'defaults', + get: function () { + return DEFAULTS; + } + }]); + + return StickySidebar; + }(); + + return StickySidebar; + }(); + + exports.default = StickySidebar; + + + // Global + // ------------------------- + window.StickySidebar = StickySidebar; + }); + }); + + var stickySidebar$1 = unwrapExports(stickySidebar); + + exports.default = stickySidebar$1; + exports.__moduleExports = stickySidebar; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); //# sourceMappingURL=sticky-sidebar.js.map diff --git a/dist/sticky-sidebar.js.map b/dist/sticky-sidebar.js.map index a73d5cd..ba314fa 100644 --- a/dist/sticky-sidebar.js.map +++ b/dist/sticky-sidebar.js.map @@ -1 +1 @@ -{"version":3,"file":"sticky-sidebar.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"sticky-sidebar.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/dist/sticky-sidebar.min.js b/dist/sticky-sidebar.min.js index 1462f11..222be65 100644 --- a/dist/sticky-sidebar.min.js +++ b/dist/sticky-sidebar.min.js @@ -1,8 +1,8 @@ /** * sticky-sidebar - A JavaScript plugin for making smart and high performance. - * @version v3.3.1 + * @version v3.3.5 * @link https://github.com/abouolia/sticky-sidebar * @author Ahmed Bouhuolia * @license The MIT License (MIT) **/ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.StickySidebar={})}(this,function(t){"use strict";"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var e,i,n=(function(t,e){(function(t){Object.defineProperty(t,"__esModule",{value:!0});var l,n,e=function(){function n(t,e){for(var i=0;i=t.containerBottom?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):i>=t.containerTop&&(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):e+t.translateY<=n?(t.translateY=n-e,o="VIEWPORT-BOTTOM"):t.containerTop+t.translateY<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getAffixTypeScrollingUp",value:function(){var t=this.dimensions,e=t.sidebarHeight+t.containerTop,i=t.viewportTop+t.topSpacing,n=t.viewportBottom-t.bottomSpacing,o=this.affixedType;return i<=t.translateY+t.containerTop?(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):this.isSidebarFitsViewport()||t.containerTop<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getStyle",value:function(t){if(void 0!==t){var e={inner:{},outer:{}},i=this.dimensions;switch(t){case"VIEWPORT-TOP":e.inner={position:"fixed",top:i.topSpacing,left:i.sidebarLeft-i.viewportLeft,width:i.sidebarWidth};break;case"VIEWPORT-BOTTOM":e.inner={position:"fixed",top:"auto",left:i.sidebarLeft,bottom:i.bottomSpacing,width:i.sidebarWidth};break;case"CONTAINER-BOTTOM":case"VIEWPORT-UNBOTTOM":var n=this._getTranslate(0,i.translateY+"px");e.inner=n?{transform:n}:{position:"absolute",top:i.translateY,width:i.sidebarWidth}}switch(t){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":e.outer={height:i.sidebarHeight,position:"relative"}}return e.outer=c.extend({height:"",position:""},e.outer),e.inner=c.extend({position:"relative",top:"",left:"",bottom:"",width:"",transform:""},e.inner),e}}},{key:"stickyPosition",value:function(t){if(!this._breakpoint){t=this._reStyle||t||!1,this.options.topSpacing,this.options.bottomSpacing;var e=this.getAffixType(),i=this._getStyle(e);if((this.affixedType!=e||t)&&e){var n="affix."+e.toLowerCase().replace("viewport-","")+l;for(var o in c.eventTrigger(this.sidebar,n),"STATIC"===e?c.removeClass(this.sidebar,this.options.stickyClass):c.addClass(this.sidebar,this.options.stickyClass),i.outer){var s="number"==typeof i.outer[o]?"px":"";this.sidebar.style[o]=i.outer[o]+s}for(var r in i.inner){var a="number"==typeof i.inner[r]?"px":"";this.sidebarInner.style[r]=i.inner[r]+a}var p="affixed."+e.toLowerCase().replace("viewport-","")+l;c.eventTrigger(this.sidebar,p)}else this._initialized&&(this.sidebarInner.style.left=i.inner.left);this.affixedType=e}}},{key:"_widthBreakpoint",value:function(){window.innerWidth<=this.options.minWidth?(this._breakpoint=!0,this.affixedType="STATIC",this.sidebar.removeAttribute("style"),c.removeClass(this.sidebar,this.options.stickyClass),this.sidebarInner.removeAttribute("style")):this._breakpoint=!1}},{key:"updateSticky",value:function(){var t,e=this,i=0=t.containerBottom?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):i>=t.containerTop&&(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):e+t.translateY<=n?(t.translateY=n-e,o="VIEWPORT-BOTTOM"):t.containerTop+t.translateY<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getAffixTypeScrollingUp",value:function(){var t=this.dimensions,e=t.sidebarHeight+t.containerTop,i=t.viewportTop+t.topSpacing,n=t.viewportBottom-t.bottomSpacing,o=this.affixedType;return i<=t.translateY+t.containerTop?(t.translateY=i-t.containerTop,o="VIEWPORT-TOP"):t.containerBottom<=n?(t.translateY=t.containerBottom-e,o="CONTAINER-BOTTOM"):this.isSidebarFitsViewport()||t.containerTop<=i&&0!==t.translateY&&t.maxTranslateY!==t.translateY&&(o="VIEWPORT-UNBOTTOM"),o}},{key:"_getStyle",value:function(t){if(void 0!==t){var e={inner:{},outer:{}},i=this.dimensions;if(this.options.nativeSticky){switch(t){case"VIEWPORT-TOP":e.inner={position:"sticky",top:i.topSpacing};break;case"VIEWPORT-BOTTOM":e.inner={position:"sticky",top:i.viewportHeight-i.sidebarHeight-i.bottomSpacing};break;case"CONTAINER-BOTTOM":e.inner={position:"sticky",top:"100%"};break;case"VIEWPORT-UNBOTTOM":e.inner={position:"relative",top:i.translateY}}switch(t){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":e.outer={height:"100%",position:"relative"}}}else{switch(t){case"VIEWPORT-TOP":e.inner={position:"fixed",top:i.topSpacing,left:i.sidebarLeft-i.viewportLeft,width:i.sidebarWidth};break;case"VIEWPORT-BOTTOM":e.inner={position:"fixed",top:"auto",left:i.sidebarLeft,bottom:i.bottomSpacing,width:i.sidebarWidth};break;case"CONTAINER-BOTTOM":case"VIEWPORT-UNBOTTOM":var n=this._getTranslate(0,i.translateY+"px");e.inner=n?{transform:n}:{position:"absolute",top:i.translateY,width:i.sidebarWidth}}switch(t){case"VIEWPORT-TOP":case"VIEWPORT-BOTTOM":case"VIEWPORT-UNBOTTOM":case"CONTAINER-BOTTOM":e.outer={height:i.sidebarHeight,position:"relative"}}}return e.outer=p.extend({height:"",position:""},e.outer),e.inner=p.extend({position:"relative",top:"",left:"",bottom:"",width:"",transform:""},e.inner),e}}},{key:"stickyPosition",value:function(t){if(!this._breakpoint){t=this._reStyle||t||!1;this.options.topSpacing,this.options.bottomSpacing;var e=this.getAffixType(),i=this._getStyle(e);if((this.affixedType!=e||t)&&e){var n,o,t="affix."+e.toLowerCase().replace("viewport-","")+a;for(n in p.eventTrigger(this.sidebar,t),"STATIC"===e?p.removeClass(this.sidebar,this.options.stickyClass):p.addClass(this.sidebar,this.options.stickyClass),i.outer){var s="number"==typeof i.outer[n]?"px":"";this.sidebar.style[n]=i.outer[n]+s}for(o in i.inner){var r="number"==typeof i.inner[o]?"px":"";this.sidebarInner.style[o]=i.inner[o]+r}t="affixed."+e.toLowerCase().replace("viewport-","")+a;p.eventTrigger(this.sidebar,t)}else this._initialized&&(this.sidebarInner.style.left=i.inner.left);this.affixedType=e}}},{key:"_widthBreakpoint",value:function(){window.innerWidth<=this.options.minWidth?(this._breakpoint=!0,this.affixedType="STATIC",this.sidebar.removeAttribute("style"),p.removeClass(this.sidebar,this.options.stickyClass),this.sidebarInner.removeAttribute("style")):this._breakpoint=!1}},{key:"updateSticky",value:function(){var t,e=this,i=0 * @license The MIT License (MIT) */ const StickySidebar = (() => { - + // --------------------------------- // # Define Constants // --------------------------------- // const EVENT_KEY = '.stickySidebar'; - const VERSION = '3.3.4'; - + const VERSION = '3.3.5'; + const DEFAULTS = { /** * Additional top spacing of the element when it becomes sticky. * @type {Numeric|Function} */ topSpacing: 0, - + /** * Additional bottom spacing of the element when it becomes sticky. * @type {Numeric|Function} */ bottomSpacing: 0, - + + /** + * Scroll container selector. + * @type {String|False} + */ + scrollContainerSelector: false, + /** * Container sidebar selector to know what the beginning and end of sticky element. * @type {String|False} */ containerSelector: false, - + /** * Inner wrapper selector. * @type {String} */ innerWrapperSelector: '.inner-wrapper-sticky', - + /** * The name of CSS class to apply to elements when they have become stuck. * @type {String|False} */ stickyClass: 'is-affixed', - + /** * Detect when sidebar and its container change height so re-calculate their dimensions. * @type {Boolean} */ resizeSensor: true, - + /** * The sidebar returns to its normal position if its width below this value. * @type {Numeric} */ - minWidth: false + minWidth: false, + + /** + * Use native CSS position sticky. + * @type {Boolean} + */ + nativeSticky: false }; - + // --------------------------------- // # Class Definition // --------------------------------- @@ -66,7 +78,7 @@ const StickySidebar = (() => { * @public */ class StickySidebar{ - + /** * Sticky Sidebar Constructor. * @constructor @@ -75,27 +87,34 @@ const StickySidebar = (() => { */ constructor(sidebar, options = {}){ this.options = StickySidebar.extend(DEFAULTS, options); - + // Sidebar element query if there's no one, throw error. this.sidebar = ('string' === typeof sidebar ) ? document.querySelector(sidebar) : sidebar; if( 'undefined' === typeof this.sidebar ) throw new Error("There is no specific sidebar element."); - + this.sidebarInner = false; this.container = this.sidebar.parentElement; - + + // Get scroll container, if provided + this.scrollContainer = this.options.scrollContainerSelector + ? 'string' === typeof this.options.scrollContainerSelector + ? document.querySelector(this.options.scrollContainerSelector) + : this.options.scrollContainerSelector + : undefined; + // Current Affix Type of sidebar element. this.affixedType = 'STATIC'; this.direction = 'down'; - this.support = { + this.support = { transform: false, transform3d: false }; - + this._initialized = false; this._reStyle = false; this._breakpoint = false; - + // Dimensions of sidebar, container and screen viewport. this.dimensions = { translateY: 0, @@ -109,99 +128,101 @@ const StickySidebar = (() => { containerTop: 0, containerHeight: 0, viewportHeight: 0, - viewportTop: 0, + viewportTop: 0, lastViewportTop: 0, }; - + // Bind event handlers for referencability. ['handleEvent'].forEach( (method) => { this[method] = this[method].bind(this); }); - + // Initialize sticky sidebar for first time. this.initialize(); } - + /** - * Initializes the sticky sidebar by adding inner wrapper, define its container, + * Initializes the sticky sidebar by adding inner wrapper, define its container, * min-width breakpoint, calculating dimensions, adding helper classes and inline style. * @private */ initialize(){ this._setSupportFeatures(); - + // Get sticky sidebar inner wrapper, if not found, will create one. if( this.options.innerWrapperSelector ){ this.sidebarInner = this.sidebar.querySelector(this.options.innerWrapperSelector); - + if( null === this.sidebarInner ) this.sidebarInner = false; } - + if( ! this.sidebarInner ){ let wrapper = document.createElement('div'); wrapper.setAttribute('class', 'inner-wrapper-sticky'); this.sidebar.appendChild(wrapper); - + while( this.sidebar.firstChild != wrapper ) wrapper.appendChild(this.sidebar.firstChild); - + this.sidebarInner = this.sidebar.querySelector('.inner-wrapper-sticky'); } - + // Container wrapper of the sidebar. if( this.options.containerSelector ){ let containers = document.querySelectorAll(this.options.containerSelector); containers = Array.prototype.slice.call(containers); - + containers.forEach((container, item) => { if( ! container.contains(this.sidebar) ) return; this.container = container; }); - + if( ! containers.length ) throw new Error("The container does not contains on the sidebar."); } - + // If top/bottom spacing is not function parse value to integer. if( 'function' !== typeof this.options.topSpacing ) this.options.topSpacing = parseInt(this.options.topSpacing) || 0; - + if( 'function' !== typeof this.options.bottomSpacing ) this.options.bottomSpacing = parseInt(this.options.bottomSpacing) || 0; - + // Breakdown sticky sidebar if screen width below `options.minWidth`. this._widthBreakpoint(); - + // Calculate dimensions of sidebar, container and viewport. this.calcDimensions(); - + // Affix sidebar in proper position. this.stickyPosition(); - + // Bind all events. this.bindEvents(); - + // Inform other properties the sticky sidebar is initialized. this._initialized = true; } - + /** * Bind all events of sticky sidebar plugin. * @protected */ bindEvents(){ window.addEventListener('resize', this, {passive: true, capture: false}); - window.addEventListener('scroll', this, {passive: true, capture: false}); - + (this.scrollContainer || window).addEventListener('scroll', this, {passive: true, capture: false}); + this.sidebar.addEventListener('update' + EVENT_KEY, this); - - if( this.options.resizeSensor && 'undefined' !== typeof ResizeSensor ){ - new ResizeSensor(this.sidebarInner, this.handleEvent); - new ResizeSensor(this.container, this.handleEvent); + + if( this.options.resizeSensor && 'undefined' !== typeof ResizeObserver ){ + this.resizeObserver = new ResizeObserver(() => this.handleEvent({ type: 'resize' })); + this.resizeObserver.observe(this.sidebarInner); + this.resizeObserver.observe(this.container); + if( this.scrollContainer ) this.resizeObserver.observe(this.scrollContainer); } } - + /** * Handles all events of the plugin. * @param {Object} event - Event object passed from listener. @@ -209,7 +230,7 @@ const StickySidebar = (() => { handleEvent(event){ this.updateSticky(event); } - + /** * Calculates dimensions of sidebar, container and screen viewpoint * @public @@ -217,52 +238,67 @@ const StickySidebar = (() => { calcDimensions(){ if( this._breakpoint ) return; var dims = this.dimensions; - + // Container of sticky sidebar dimensions. - dims.containerTop = StickySidebar.offsetRelative(this.container).top; + dims.containerTop = StickySidebar.offsetRelative(this.container, this.scrollContainer).top; dims.containerHeight = this.container.clientHeight; dims.containerBottom = dims.containerTop + dims.containerHeight; - + // Sidebar dimensions. dims.sidebarHeight = this.sidebarInner.offsetHeight; dims.sidebarWidth = this.sidebarInner.offsetWidth; - + // Screen viewport dimensions. - dims.viewportHeight = window.innerHeight; + if( this.scrollContainer ){ + var scrollContainerPaddingTop = parseFloat(getComputedStyle(this.scrollContainer).paddingTop) || 0 + var scrollContainerPaddingBottom = parseFloat(getComputedStyle(this.scrollContainer).paddingBottom) || 0 + + dims.viewportHeight = this.scrollContainer.clientHeight - scrollContainerPaddingTop - scrollContainerPaddingBottom; + } else { + dims.viewportHeight = window.innerHeight; + } // Maximum sidebar translate Y. dims.maxTranslateY = dims.containerHeight - dims.sidebarHeight; this._calcDimensionsWithScroll(); } - + /** * Some dimensions values need to be up-to-date when scrolling the page. * @private */ _calcDimensionsWithScroll(){ var dims = this.dimensions; - - dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar).left; - - dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop; + + dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar, this.scrollContainer).left; + + if( this.scrollContainer ){ + var scrollContainerPaddingTop = parseFloat(getComputedStyle(this.scrollContainer).paddingTop) || 0 + var scrollContainerPaddingLeft = parseFloat(getComputedStyle(this.scrollContainer).paddingLeft) || 0 + + dims.viewportTop = this.scrollContainer.scrollTop + scrollContainerPaddingTop; + dims.viewportLeft = this.scrollContainer.scrollLeft + scrollContainerPaddingLeft; + } else { + dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop; + dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + } dims.viewportBottom = dims.viewportTop + dims.viewportHeight; - dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft; - + dims.topSpacing = this.options.topSpacing; dims.bottomSpacing = this.options.bottomSpacing; - + if( 'function' === typeof dims.topSpacing ) dims.topSpacing = parseInt(dims.topSpacing(this.sidebar)) || 0; - + if( 'function' === typeof dims.bottomSpacing ) dims.bottomSpacing = parseInt(dims.bottomSpacing(this.sidebar)) || 0; - + if( 'VIEWPORT-TOP' === this.affixedType ){ // Adjust translate Y in the case decrease top spacing value. if( dims.topSpacing < dims.lastTopSpacing ){ dims.translateY += dims.lastTopSpacing - dims.topSpacing; - this._reStyle = true; + this._reStyle = true; } } else if( 'VIEWPORT-BOTTOM' === this.affixedType ){ // Adjust translate Y in the case decrease bottom spacing value. @@ -271,11 +307,11 @@ const StickySidebar = (() => { this._reStyle = true; } } - + dims.lastTopSpacing = dims.topSpacing; dims.lastBottomSpacing = dims.bottomSpacing; } - + /** * Determine whether the sidebar is bigger than viewport. * @public @@ -286,21 +322,21 @@ const StickySidebar = (() => { let offset = this.scrollDirection === 'down' ? dims.lastBottomSpacing : dims.lastTopSpacing; return this.dimensions.sidebarHeight + offset < this.dimensions.viewportHeight; } - + /** * Observe browser scrolling direction top and down. */ observeScrollDir(){ var dims = this.dimensions; if( dims.lastViewportTop === dims.viewportTop ) return; - + var furthest = 'down' === this.direction ? Math.min : Math.max; - + // If the browser is scrolling not in the same direction. if( dims.viewportTop === furthest(dims.viewportTop, dims.lastViewportTop) ) this.direction = 'down' === this.direction ? 'up' : 'down'; } - + /** * Gets affix type of sidebar according to current scroll top and scrolling direction. * @public @@ -316,7 +352,7 @@ const StickySidebar = (() => { dims.translateY = 0; affixType = 'STATIC'; } else { - affixType = ( 'up' === this.direction ) ? + affixType = ( 'up' === this.direction ) ? this._getAffixTypeScrollingUp() : this._getAffixTypeScrollingDown(); } @@ -324,7 +360,7 @@ const StickySidebar = (() => { dims.translateY = Math.max(0, dims.translateY); dims.translateY = Math.min(dims.containerHeight, dims.translateY); dims.translateY = Math.round(dims.translateY); - + dims.lastViewportTop = dims.viewportTop; return affixType; } @@ -340,11 +376,11 @@ const StickySidebar = (() => { var colliderTop = dims.viewportTop + dims.topSpacing; var colliderBottom = dims.viewportBottom - dims.bottomSpacing; var affixType = this.affixedType; - + if( this.isSidebarFitsViewport() ){ if( dims.sidebarHeight + colliderTop >= dims.containerBottom ){ dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; + affixType = 'CONTAINER-BOTTOM'; } else if( colliderTop >= dims.containerTop ){ dims.translateY = colliderTop - dims.containerTop; @@ -352,13 +388,13 @@ const StickySidebar = (() => { } } else { if( dims.containerBottom <= colliderBottom ){ - dims.translateY = dims.containerBottom - sidebarBottom; - affixType = 'CONTAINER-BOTTOM'; + dims.translateY = dims.containerBottom - sidebarBottom; + affixType = 'CONTAINER-BOTTOM'; } else if( sidebarBottom + dims.translateY <= colliderBottom ){ dims.translateY = colliderBottom - sidebarBottom; affixType = 'VIEWPORT-BOTTOM'; - + } else if( dims.containerTop + dims.translateY <= colliderTop && (0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) ){ affixType = 'VIEWPORT-UNBOTTOM'; @@ -367,7 +403,7 @@ const StickySidebar = (() => { return affixType; } - + /** * Get affix type while scrolling up. * @private @@ -383,24 +419,24 @@ const StickySidebar = (() => { if( colliderTop <= dims.translateY + dims.containerTop ){ dims.translateY = colliderTop - dims.containerTop; affixType = 'VIEWPORT-TOP'; - + } else if( dims.containerBottom <= colliderBottom ){ dims.translateY = dims.containerBottom - sidebarBottom; affixType = 'CONTAINER-BOTTOM'; } else if( ! this.isSidebarFitsViewport() ){ - if( dims.containerTop <= colliderTop && + if( dims.containerTop <= colliderTop && (0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) ){ affixType = 'VIEWPORT-UNBOTTOM'; - } + } } return affixType; } /** - * Gets inline style of sticky sidebar wrapper and inner wrapper according + * Gets inline style of sticky sidebar wrapper and inner wrapper according * to its affix type. * @private * @param {String} affixType - Affix type of sticky sidebar. @@ -408,46 +444,72 @@ const StickySidebar = (() => { */ _getStyle(affixType){ if( 'undefined' === typeof affixType ) return; - + var style = {inner: {}, outer: {}}; var dims = this.dimensions; - - switch( affixType ){ - case 'VIEWPORT-TOP': - style.inner = {position: 'fixed', top: dims.topSpacing, - left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth}; - break; - case 'VIEWPORT-BOTTOM': - style.inner = {position: 'fixed', top: 'auto', left: dims.sidebarLeft, - bottom: dims.bottomSpacing, width: dims.sidebarWidth}; - break; - case 'CONTAINER-BOTTOM': - case 'VIEWPORT-UNBOTTOM': - let translate = this._getTranslate(0, dims.translateY + 'px'); - - if( translate ) - style.inner = {transform: translate}; - else - style.inner = {position: 'absolute', top: dims.translateY, width: dims.sidebarWidth}; - break; - } - - switch( affixType ){ - case 'VIEWPORT-TOP': - case 'VIEWPORT-BOTTOM': - case 'VIEWPORT-UNBOTTOM': - case 'CONTAINER-BOTTOM': - style.outer = {height: dims.sidebarHeight, position: 'relative'}; - break; + + if( this.options.nativeSticky ){ + switch ( affixType ) { + case 'VIEWPORT-TOP': + style.inner = {position: 'sticky', top: dims.topSpacing}; + break; + case 'VIEWPORT-BOTTOM': + style.inner = {position: 'sticky', top: dims.viewportHeight - dims.sidebarHeight - dims.bottomSpacing}; + break; + case 'CONTAINER-BOTTOM': + style.inner = {position: 'sticky', top: '100%'}; + break; + case 'VIEWPORT-UNBOTTOM': + style.inner = {position: 'relative', top: dims.translateY}; + break; + } + + switch (affixType) { + case 'VIEWPORT-TOP': + case 'VIEWPORT-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + case 'CONTAINER-BOTTOM': + style.outer = {height: '100%', position: 'relative'}; + break; + } + } else { + switch( affixType ){ + case 'VIEWPORT-TOP': + style.inner = {position: 'fixed', top: dims.topSpacing, + left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth}; + break; + case 'VIEWPORT-BOTTOM': + style.inner = {position: 'fixed', top: 'auto', left: dims.sidebarLeft, + bottom: dims.bottomSpacing, width: dims.sidebarWidth}; + break; + case 'CONTAINER-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + let translate = this._getTranslate(0, dims.translateY + 'px'); + + if( translate ) + style.inner = {transform: translate}; + else + style.inner = {position: 'absolute', top: dims.translateY, width: dims.sidebarWidth}; + break; + } + + switch( affixType ){ + case 'VIEWPORT-TOP': + case 'VIEWPORT-BOTTOM': + case 'VIEWPORT-UNBOTTOM': + case 'CONTAINER-BOTTOM': + style.outer = {height: dims.sidebarHeight, position: 'relative'}; + break; + } } - + style.outer = StickySidebar.extend({height: '', position: ''}, style.outer); style.inner = StickySidebar.extend({position: 'relative', top: '', left: '', bottom: '', width: '', transform: ''}, style.inner); - + return style; } - + /** * Cause the sidebar to be sticky according to affix type by adding inline * style, adding helper class and trigger events. @@ -457,53 +519,53 @@ const StickySidebar = (() => { */ stickyPosition(force){ if( this._breakpoint ) return; - + force = this._reStyle || force || false; - + var offsetTop = this.options.topSpacing; var offsetBottom = this.options.bottomSpacing; - + var affixType = this.getAffixType(); var style = this._getStyle(affixType); - + if( (this.affixedType != affixType || force) && affixType ){ let affixEvent = 'affix.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; StickySidebar.eventTrigger(this.sidebar, affixEvent); - + if( 'STATIC' === affixType ) StickySidebar.removeClass(this.sidebar, this.options.stickyClass); else StickySidebar.addClass(this.sidebar, this.options.stickyClass); - + for( let key in style.outer ){ let unit = ('number' === typeof style.outer[key]) ? 'px' : ''; this.sidebar.style[key] = style.outer[key] + unit; } - + for( let key in style.inner ){ let unit = ('number' === typeof style.inner[key]) ? 'px' : ''; this.sidebarInner.style[key] = style.inner[key] + unit; } - + let affixedEvent = 'affixed.'+ affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY; StickySidebar.eventTrigger(this.sidebar, affixedEvent); } else { if( this._initialized ) this.sidebarInner.style.left = style.inner.left; } - + this.affixedType = affixType; } - + /** * Breakdown sticky sidebar when window width is below `options.minWidth` value. * @protected */ _widthBreakpoint(){ - + if( window.innerWidth <= this.options.minWidth ){ this._breakpoint = true; this.affixedType = 'STATIC'; - + this.sidebar.removeAttribute('style'); StickySidebar.removeClass(this.sidebar, this.options.stickyClass); this.sidebarInner.removeAttribute('style'); @@ -511,31 +573,31 @@ const StickySidebar = (() => { this._breakpoint = false; } } - + /** - * Switches between functions stack for each event type, if there's no + * Switches between functions stack for each event type, if there's no * event, it will re-initialize sticky sidebar. * @public */ updateSticky(event = {}){ if( this._running ) return; this._running = true; - + ((eventType) => { requestAnimationFrame(() => { switch( eventType ){ // When browser is scrolling and re-calculate just dimensions - // within scroll. + // within scroll. case 'scroll': this._calcDimensionsWithScroll(); this.observeScrollDir(); this.stickyPosition(); break; - + // When browser is resizing or there's no event, observe width // breakpoint and re-calculate dimensions. case 'resize': - default: + default: this._widthBreakpoint(); this.calcDimensions(); this.stickyPosition(true); @@ -545,18 +607,18 @@ const StickySidebar = (() => { }); })(event.type); } - + /** * Set browser support features to the public property. * @private */ _setSupportFeatures(){ var support = this.support; - + support.transform = StickySidebar.supportTransform(); support.transform3d = StickySidebar.supportTransform(true); } - + /** * Get translate value, if the browser supports transfrom3d, it will adopt it. * and the same with translate. if browser doesn't support both return false. @@ -570,7 +632,7 @@ const StickySidebar = (() => { else if( this.support.translate ) return 'translate('+ y +', '+ x +')'; else return false; } - + /** * Destroy sticky sidebar plugin. * @public @@ -578,29 +640,30 @@ const StickySidebar = (() => { destroy(){ window.removeEventListener('resize', this, {capture: false}); window.removeEventListener('scroll', this, {capture: false}); - + this.sidebar.classList.remove(this.options.stickyClass); this.sidebar.style.minHeight = ''; - + this.sidebar.removeEventListener('update' + EVENT_KEY, this); - + var styleReset = {inner: {}, outer: {}}; - + styleReset.inner = {position: '', top: '', left: '', bottom: '', width: '', transform: ''}; styleReset.outer = {height: '', position: ''}; - + for( let key in styleReset.outer ) this.sidebar.style[key] = styleReset.outer[key]; - + for( let key in styleReset.inner ) this.sidebarInner.style[key] = styleReset.inner[key]; - - if( this.options.resizeSensor && 'undefined' !== typeof ResizeSensor ){ - ResizeSensor.detach(this.sidebarInner, this.handleEvent); - ResizeSensor.detach(this.container, this.handleEvent); + + if( this.options.resizeSensor && 'undefined' !== typeof ResizeObserver && this.resizeObserver ){ + this.resizeObserver.unobserve(this.sidebarInner); + this.resizeObserver.unobserve(this.container); + if( this.scrollContainer ) this.resizeObserver.unobserve(this.scrollContainer); } } - + /** * Determine if the browser supports CSS transform feature. * @function @@ -615,7 +678,7 @@ const StickySidebar = (() => { prefixes = ['Webkit', 'Moz', 'O', 'ms'], support = document.createElement('support'), style = support.style; - + (property + ' ' + prefixes.join(upper + ' ') + upper).split(' ').forEach(function(property, i) { if (style[property] !== undefined) { result = property; @@ -624,13 +687,13 @@ const StickySidebar = (() => { }); return result; } - + /** * Trigger custom event. * @static * @param {DOMObject} element - Target element on the DOM. * @param {String} eventName - Event name. - * @param {Object} data - + * @param {Object} data - */ static eventTrigger(element, eventName, data){ try{ @@ -641,7 +704,7 @@ const StickySidebar = (() => { } element.dispatchEvent(event); } - + /** * Extend options object with defaults. * @function @@ -655,35 +718,35 @@ const StickySidebar = (() => { } return results; } - + /** * Get current coordinates left and top of specific element. * @static */ - static offsetRelative(element){ + static offsetRelative(element, scrollContainer){ var result = {left: 0, top: 0}; do{ let offsetTop = element.offsetTop; let offsetLeft = element.offsetLeft; - + if( ! isNaN(offsetTop) ) result.top += offsetTop; - + if( ! isNaN(offsetLeft) ) result.left += offsetLeft; element = ( 'BODY' === element.tagName ) ? element.parentElement : element.offsetParent; - } while(element) + } while (element !== scrollContainer && element); return result; } - + /** * Add specific class name to specific element. - * @static - * @param {ObjectDOM} element - * @param {String} className + * @static + * @param {ObjectDOM} element + * @param {String} className */ static addClass(element, className){ if( ! StickySidebar.hasClass(element, className) ){ @@ -693,12 +756,12 @@ const StickySidebar = (() => { element.className += ' ' + className; } } - + /** * Remove specific class name to specific element * @static - * @param {ObjectDOM} element - * @param {String} className + * @param {ObjectDOM} element + * @param {String} className */ static removeClass(element, className){ if( StickySidebar.hasClass(element, className) ){ @@ -712,8 +775,8 @@ const StickySidebar = (() => { /** * Determine weather the element has specific class name. * @static - * @param {ObjectDOM} element - * @param {String} className + * @param {ObjectDOM} element + * @param {String} className */ static hasClass(element, className){ if (element.classList) @@ -725,18 +788,18 @@ const StickySidebar = (() => { /** * Gets default values of configuration options. * @static - * @return {Object} + * @return {Object} */ static get defaults(){ return DEFAULTS; } } - + return StickySidebar; })(); - + export default StickySidebar; - + // Global // ------------------------- - window.StickySidebar = StickySidebar; \ No newline at end of file + window.StickySidebar = StickySidebar;