diff --git a/.gitignore b/.gitignore index 2f4628a..36e1259 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules -dist .tmp .sass-cache bower_components diff --git a/dist/maps/stickies.jquery.min.js.map b/dist/maps/stickies.jquery.min.js.map new file mode 100644 index 0000000..a773de9 --- /dev/null +++ b/dist/maps/stickies.jquery.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["main.js","jquery-plugin.js"],"names":["$","window","document","undefined","StickyHeaders","el","options","this","element","stuckHeadersHeight","listOffset","getBoundingClientRect","top","headers","Array","prototype","map","call","querySelectorAll","headerSelector","header","i","clientRect","clone","cloneNode","classList","add","style","height","headerContainerHeight","dataset","index","bottom","bind","_createHeaderContainer","addEventListener","onScroll","Plugin","init","SCROLL_WIDTH","scrollDiv","createElement","className","body","appendChild","scrollbarWidth","offsetWidth","clientWidth","removeChild","SCROLL_STEP_DEFAULT","LINE_HEIGHT","headerWrap","right","headerContainer","onHeaderActivate","onHeaderScroll","parentNode","insertBefore","ev","target","contains","parseInt","targetHeader","scrollOffset","scrollTop","shiftAmount","forEach","isWithinHeaderContainer","transform","scrollDelta","deltaMode","WheelEvent","DOM_DELTA_PIXEL","deltaY","DOM_DELTA_LINE","preventDefault","pluginName","akno","destroy","removeData","fn","args","arguments","dataKey","each","data","instance","isFunction","apply","slice","jQuery"],"mappings":"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","file":"stickies.jquery.min.js","sourcesContent":["/* global WheelEvent */\n'use strict';\n\nvar SCROLL_WIDTH = (function() {\n var scrollDiv = document.createElement('div');\n scrollDiv.className = 'js-scrollbar-measure';\n document.body.appendChild(scrollDiv);\n var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n document.body.removeChild(scrollDiv);\n return scrollbarWidth;\n})();\n\nvar SCROLL_STEP_DEFAULT = 50;\nvar LINE_HEIGHT = 16;\n\n/**\n * This plugin enables sticky section headings on a list element.\n *\n * Supported browsers: Chrome, FF, Safari, IE11+\n *\n * @param el - list dom element\n * @param options - an option object. Supports:\n * - headerSelector - a selector string matching header elements\n */\nfunction StickyHeaders(el, options) {\n this.element = el;\n this.options = options;\n this.stuckHeadersHeight = 0;\n\n var listOffset = el.getBoundingClientRect().top;\n\n // this.headers contains clone elements references and cached dimensions for faster scroll handling\n this.headers = Array.prototype.map.call(el.querySelectorAll(this.options.headerSelector), (function(header, i) {\n var clientRect = header.getBoundingClientRect();\n var clone = header.cloneNode(true);\n clone.classList.add('stickies-header', 'is-stuck');\n // explicitly define the height for the clone, just in case it was applied on the original element\n // via a selector which is no longer affecting the clone\n //\n clone.style.height = clientRect.height + 'px';\n // TODO all clones must be of equal height\n this.headerContainerHeight = clientRect.height;\n\n clone.dataset.index = i;\n return {\n top: clientRect.top - listOffset,\n bottom: clientRect.bottom - listOffset,\n height: clientRect.height,\n el: clone\n };\n }).bind(this));\n\n this._createHeaderContainer();\n\n el.addEventListener('scroll', this.onScroll.bind(this));\n}\n\nStickyHeaders.prototype._createHeaderContainer = function() {\n var header = document.createElement('div');\n header.className = 'stickies-container';\n\n var headerWrap = document.createElement('div');\n headerWrap.className = 'stickies-container-inner';\n headerWrap.style.right = SCROLL_WIDTH + 'px';\n headerWrap.style.height = this.headerContainerHeight + 'px';\n header.appendChild(headerWrap);\n\n var headerContainer = this.headerContainer = document.createElement('div');\n headerWrap.appendChild(headerContainer);\n\n header.addEventListener('click', this.onHeaderActivate.bind(this));\n header.addEventListener('wheel', this.onHeaderScroll.bind(this));\n\n this.element.parentNode.insertBefore(header, this.element);\n};\n\nStickyHeaders.prototype.onHeaderActivate = function(ev) {\n if (ev.target.classList.contains('is-stuck')) {\n var index = parseInt(ev.target.dataset.index, 10);\n var targetHeader = this.headers[index];\n var scrollOffset = 0;\n for (var i = 0; i <= index; i++) {\n scrollOffset += this.headers[i].height;\n }\n\n // Scrollable area is reduces by the height of stuck headers.\n // Need to account for that when jumping to the new position.\n this.element.scrollTop = targetHeader.bottom - scrollOffset;\n }\n};\n\nStickyHeaders.prototype.onScroll = function() {\n var scrollTop = this.element.scrollTop;\n var shiftAmount = 0;\n\n this.headers.forEach(function(header) {\n if (!header.el.parentNode) {\n if (header.top <= scrollTop) {\n this.headerContainer.appendChild(header.el);\n this.stuckHeadersHeight -= header.height;\n }\n } else if (header.el.parentNode) {\n if (header.top >= scrollTop) {\n this.headerContainer.removeChild(header.el);\n this.stuckHeadersHeight += header.height;\n }\n }\n\n if (this.isWithinHeaderContainer(header, scrollTop)) {\n // the distance between the top of the scrollable area and the header top\n // minus the height of the header container gives the shift amount\n // for the stuck headers on in order to have an effect of a 'replacement'\n // of the old header with a new one\n shiftAmount = (header.top - scrollTop) - this.headerContainerHeight;\n }\n }, this);\n\n shiftAmount += this.stuckHeadersHeight + this.headerContainerHeight;\n\n this.headerContainer.style.transform = 'translateY(' + shiftAmount + 'px)';\n};\n\nStickyHeaders.prototype.onHeaderScroll = function(ev) {\n var scrollDelta = 0;\n switch (ev.deltaMode) {\n case WheelEvent.DOM_DELTA_PIXEL:\n scrollDelta = ev.deltaY;\n break;\n case WheelEvent.DOM_DELTA_LINE:\n scrollDelta = LINE_HEIGHT * ev.deltaY;\n break;\n default:\n scrollDelta = SCROLL_STEP_DEFAULT;\n }\n this.element.scrollTop += scrollDelta;\n // prevent the viewport from scrolling\n ev.preventDefault();\n};\n\nStickyHeaders.prototype.isWithinHeaderContainer = function(header, scrollTop) {\n return header.top > scrollTop && header.top <= this.headerContainerHeight + scrollTop;\n};\n","var pluginName = 'stickies';\n\nfunction Plugin(element, options) {\n this.element = element;\n this.options = options;\n this.init();\n}\n\nPlugin.prototype.init = function() {\n this.akno = new StickyHeaders(this.element, this.options);\n};\n\nPlugin.prototype.destroy = function() {\n this.akno.destroy();\n $.removeData(this.element, 'plugin_' + pluginName);\n this.element = null;\n};\n\n$.fn[pluginName] = function(options) {\n var args = arguments;\n var dataKey = 'plugin_' + pluginName;\n if (options === undefined || typeof options === 'object') {\n return this.each(function() {\n if (!$.data(this, dataKey)) {\n $.data(this, dataKey, new Plugin(this, options));\n }\n });\n } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {\n return this.each(function() {\n var instance = $.data(this, dataKey);\n if (instance instanceof Plugin) {\n // call with the widget instance if not on the plugin\n if(!$.isFunction(instance[options]) && $.isFunction(instance.akno[options])) {\n instance = instance.akno;\n }\n instance[options].apply(instance, Array.prototype.slice.call(args, 1));\n }\n });\n }\n};\n"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/dist/maps/stickies.min.js.map b/dist/maps/stickies.min.js.map new file mode 100644 index 0000000..168291c --- /dev/null +++ b/dist/maps/stickies.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["stickies.js"],"names":["window","document","StickyHeaders","el","options","this","element","stuckHeadersHeight","listOffset","getBoundingClientRect","top","headers","Array","prototype","map","call","querySelectorAll","headerSelector","header","i","clientRect","clone","cloneNode","classList","add","style","height","headerContainerHeight","dataset","index","bottom","bind","_createHeaderContainer","addEventListener","onScroll","SCROLL_WIDTH","scrollDiv","createElement","className","body","appendChild","scrollbarWidth","offsetWidth","clientWidth","removeChild","SCROLL_STEP_DEFAULT","LINE_HEIGHT","headerWrap","right","headerContainer","onHeaderActivate","onHeaderScroll","parentNode","insertBefore","ev","target","contains","parseInt","targetHeader","scrollOffset","scrollTop","shiftAmount","forEach","isWithinHeaderContainer","transform","scrollDelta","deltaMode","WheelEvent","DOM_DELTA_PIXEL","deltaY","DOM_DELTA_LINE","preventDefault"],"mappings":"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","file":"stickies.min.js","sourcesContent":[";(function(window, document, undefined) {/* global WheelEvent */\n'use strict';\n\nvar SCROLL_WIDTH = (function() {\n var scrollDiv = document.createElement('div');\n scrollDiv.className = 'js-scrollbar-measure';\n document.body.appendChild(scrollDiv);\n var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n document.body.removeChild(scrollDiv);\n return scrollbarWidth;\n})();\n\nvar SCROLL_STEP_DEFAULT = 50;\nvar LINE_HEIGHT = 16;\n\n/**\n * This plugin enables sticky section headings on a list element.\n *\n * Supported browsers: Chrome, FF, Safari, IE11+\n *\n * @param el - list dom element\n * @param options - an option object. Supports:\n * - headerSelector - a selector string matching header elements\n */\nfunction StickyHeaders(el, options) {\n this.element = el;\n this.options = options;\n this.stuckHeadersHeight = 0;\n\n var listOffset = el.getBoundingClientRect().top;\n\n // this.headers contains clone elements references and cached dimensions for faster scroll handling\n this.headers = Array.prototype.map.call(el.querySelectorAll(this.options.headerSelector), (function(header, i) {\n var clientRect = header.getBoundingClientRect();\n var clone = header.cloneNode(true);\n clone.classList.add('stickies-header', 'is-stuck');\n // explicitly define the height for the clone, just in case it was applied on the original element\n // via a selector which is no longer affecting the clone\n //\n clone.style.height = clientRect.height + 'px';\n // TODO all clones must be of equal height\n this.headerContainerHeight = clientRect.height;\n\n clone.dataset.index = i;\n return {\n top: clientRect.top - listOffset,\n bottom: clientRect.bottom - listOffset,\n height: clientRect.height,\n el: clone\n };\n }).bind(this));\n\n this._createHeaderContainer();\n\n el.addEventListener('scroll', this.onScroll.bind(this));\n}\n\nStickyHeaders.prototype._createHeaderContainer = function() {\n var header = document.createElement('div');\n header.className = 'stickies-container';\n\n var headerWrap = document.createElement('div');\n headerWrap.className = 'stickies-container-inner';\n headerWrap.style.right = SCROLL_WIDTH + 'px';\n headerWrap.style.height = this.headerContainerHeight + 'px';\n header.appendChild(headerWrap);\n\n var headerContainer = this.headerContainer = document.createElement('div');\n headerWrap.appendChild(headerContainer);\n\n header.addEventListener('click', this.onHeaderActivate.bind(this));\n header.addEventListener('wheel', this.onHeaderScroll.bind(this));\n\n this.element.parentNode.insertBefore(header, this.element);\n};\n\nStickyHeaders.prototype.onHeaderActivate = function(ev) {\n if (ev.target.classList.contains('is-stuck')) {\n var index = parseInt(ev.target.dataset.index, 10);\n var targetHeader = this.headers[index];\n var scrollOffset = 0;\n for (var i = 0; i <= index; i++) {\n scrollOffset += this.headers[i].height;\n }\n\n // Scrollable area is reduces by the height of stuck headers.\n // Need to account for that when jumping to the new position.\n this.element.scrollTop = targetHeader.bottom - scrollOffset;\n }\n};\n\nStickyHeaders.prototype.onScroll = function() {\n var scrollTop = this.element.scrollTop;\n var shiftAmount = 0;\n\n this.headers.forEach(function(header) {\n if (!header.el.parentNode) {\n if (header.top <= scrollTop) {\n this.headerContainer.appendChild(header.el);\n this.stuckHeadersHeight -= header.height;\n }\n } else if (header.el.parentNode) {\n if (header.top >= scrollTop) {\n this.headerContainer.removeChild(header.el);\n this.stuckHeadersHeight += header.height;\n }\n }\n\n if (this.isWithinHeaderContainer(header, scrollTop)) {\n // the distance between the top of the scrollable area and the header top\n // minus the height of the header container gives the shift amount\n // for the stuck headers on in order to have an effect of a 'replacement'\n // of the old header with a new one\n shiftAmount = (header.top - scrollTop) - this.headerContainerHeight;\n }\n }, this);\n\n shiftAmount += this.stuckHeadersHeight + this.headerContainerHeight;\n\n this.headerContainer.style.transform = 'translateY(' + shiftAmount + 'px)';\n};\n\nStickyHeaders.prototype.onHeaderScroll = function(ev) {\n var scrollDelta = 0;\n switch (ev.deltaMode) {\n case WheelEvent.DOM_DELTA_PIXEL:\n scrollDelta = ev.deltaY;\n break;\n case WheelEvent.DOM_DELTA_LINE:\n scrollDelta = LINE_HEIGHT * ev.deltaY;\n break;\n default:\n scrollDelta = SCROLL_STEP_DEFAULT;\n }\n this.element.scrollTop += scrollDelta;\n // prevent the viewport from scrolling\n ev.preventDefault();\n};\n\nStickyHeaders.prototype.isWithinHeaderContainer = function(header, scrollTop) {\n return header.top > scrollTop && header.top <= this.headerContainerHeight + scrollTop;\n};\n})(window, window.document);"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/dist/scripts/stickies.jquery.js b/dist/scripts/stickies.jquery.js new file mode 100644 index 0000000..ed7e06e --- /dev/null +++ b/dist/scripts/stickies.jquery.js @@ -0,0 +1,184 @@ +;(function($, window, document, undefined) {/* global WheelEvent */ +'use strict'; + +var SCROLL_WIDTH = (function() { + var scrollDiv = document.createElement('div'); + scrollDiv.className = 'js-scrollbar-measure'; + document.body.appendChild(scrollDiv); + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + return scrollbarWidth; +})(); + +var SCROLL_STEP_DEFAULT = 50; +var LINE_HEIGHT = 16; + +/** + * This plugin enables sticky section headings on a list element. + * + * Supported browsers: Chrome, FF, Safari, IE11+ + * + * @param el - list dom element + * @param options - an option object. Supports: + * - headerSelector - a selector string matching header elements + */ +function StickyHeaders(el, options) { + this.element = el; + this.options = options; + this.stuckHeadersHeight = 0; + + var listOffset = el.getBoundingClientRect().top; + + // this.headers contains clone elements references and cached dimensions for faster scroll handling + this.headers = Array.prototype.map.call(el.querySelectorAll(this.options.headerSelector), (function(header, i) { + var clientRect = header.getBoundingClientRect(); + var clone = header.cloneNode(true); + clone.classList.add('stickies-header', 'is-stuck'); + // explicitly define the height for the clone, just in case it was applied on the original element + // via a selector which is no longer affecting the clone + // + clone.style.height = clientRect.height + 'px'; + // TODO all clones must be of equal height + this.headerContainerHeight = clientRect.height; + + clone.dataset.index = i; + return { + top: clientRect.top - listOffset, + bottom: clientRect.bottom - listOffset, + height: clientRect.height, + el: clone + }; + }).bind(this)); + + this._createHeaderContainer(); + + el.addEventListener('scroll', this.onScroll.bind(this)); +} + +StickyHeaders.prototype._createHeaderContainer = function() { + var header = document.createElement('div'); + header.className = 'stickies-container'; + + var headerWrap = document.createElement('div'); + headerWrap.className = 'stickies-container-inner'; + headerWrap.style.right = SCROLL_WIDTH + 'px'; + headerWrap.style.height = this.headerContainerHeight + 'px'; + header.appendChild(headerWrap); + + var headerContainer = this.headerContainer = document.createElement('div'); + headerWrap.appendChild(headerContainer); + + header.addEventListener('click', this.onHeaderActivate.bind(this)); + header.addEventListener('wheel', this.onHeaderScroll.bind(this)); + + this.element.parentNode.insertBefore(header, this.element); +}; + +StickyHeaders.prototype.onHeaderActivate = function(ev) { + if (ev.target.classList.contains('is-stuck')) { + var index = parseInt(ev.target.dataset.index, 10); + var targetHeader = this.headers[index]; + var scrollOffset = 0; + for (var i = 0; i <= index; i++) { + scrollOffset += this.headers[i].height; + } + + // Scrollable area is reduces by the height of stuck headers. + // Need to account for that when jumping to the new position. + this.element.scrollTop = targetHeader.bottom - scrollOffset; + } +}; + +StickyHeaders.prototype.onScroll = function() { + var scrollTop = this.element.scrollTop; + var shiftAmount = 0; + + this.headers.forEach(function(header) { + if (!header.el.parentNode) { + if (header.top <= scrollTop) { + this.headerContainer.appendChild(header.el); + this.stuckHeadersHeight -= header.height; + } + } else if (header.el.parentNode) { + if (header.top >= scrollTop) { + this.headerContainer.removeChild(header.el); + this.stuckHeadersHeight += header.height; + } + } + + if (this.isWithinHeaderContainer(header, scrollTop)) { + // the distance between the top of the scrollable area and the header top + // minus the height of the header container gives the shift amount + // for the stuck headers on in order to have an effect of a 'replacement' + // of the old header with a new one + shiftAmount = (header.top - scrollTop) - this.headerContainerHeight; + } + }, this); + + shiftAmount += this.stuckHeadersHeight + this.headerContainerHeight; + + this.headerContainer.style.transform = 'translateY(' + shiftAmount + 'px)'; +}; + +StickyHeaders.prototype.onHeaderScroll = function(ev) { + var scrollDelta = 0; + switch (ev.deltaMode) { + case WheelEvent.DOM_DELTA_PIXEL: + scrollDelta = ev.deltaY; + break; + case WheelEvent.DOM_DELTA_LINE: + scrollDelta = LINE_HEIGHT * ev.deltaY; + break; + default: + scrollDelta = SCROLL_STEP_DEFAULT; + } + this.element.scrollTop += scrollDelta; + // prevent the viewport from scrolling + ev.preventDefault(); +}; + +StickyHeaders.prototype.isWithinHeaderContainer = function(header, scrollTop) { + return header.top > scrollTop && header.top <= this.headerContainerHeight + scrollTop; +}; + +var pluginName = 'stickies'; + +function Plugin(element, options) { + this.element = element; + this.options = options; + this.init(); +} + +Plugin.prototype.init = function() { + this.akno = new StickyHeaders(this.element, this.options); +}; + +Plugin.prototype.destroy = function() { + this.akno.destroy(); + $.removeData(this.element, 'plugin_' + pluginName); + this.element = null; +}; + +$.fn[pluginName] = function(options) { + var args = arguments; + var dataKey = 'plugin_' + pluginName; + if (options === undefined || typeof options === 'object') { + return this.each(function() { + if (!$.data(this, dataKey)) { + $.data(this, dataKey, new Plugin(this, options)); + } + }); + } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { + return this.each(function() { + var instance = $.data(this, dataKey); + if (instance instanceof Plugin) { + // call with the widget instance if not on the plugin + if(!$.isFunction(instance[options]) && $.isFunction(instance.akno[options])) { + instance = instance.akno; + } + instance[options].apply(instance, Array.prototype.slice.call(args, 1)); + } + }); + } +}; +})(jQuery, window, window.document); \ No newline at end of file diff --git a/dist/scripts/stickies.jquery.min.js b/dist/scripts/stickies.jquery.min.js new file mode 100644 index 0000000..0d4bf23 --- /dev/null +++ b/dist/scripts/stickies.jquery.min.js @@ -0,0 +1,2 @@ +!function(e,t,i,n){"use strict";function a(e,t){this.element=e,this.options=t,this.stuckHeadersHeight=0;var i=e.getBoundingClientRect().top;this.headers=Array.prototype.map.call(e.querySelectorAll(this.options.headerSelector),function(e,t){var n=e.getBoundingClientRect(),a=e.cloneNode(!0);return a.classList.add("stickies-header","is-stuck"),a.style.height=n.height+"px",this.headerContainerHeight=n.height,a.dataset.index=t,{top:n.top-i,bottom:n.bottom-i,height:n.height,el:a}}.bind(this)),this._createHeaderContainer(),e.addEventListener("scroll",this.onScroll.bind(this))}function r(e,t){this.element=e,this.options=t,this.init()}var s=function(){var e=i.createElement("div");e.className="js-scrollbar-measure",i.body.appendChild(e);var t=e.offsetWidth-e.clientWidth;return i.body.removeChild(e),t}(),o=50,h=16;a.prototype._createHeaderContainer=function(){var e=i.createElement("div");e.className="stickies-container";var t=i.createElement("div");t.className="stickies-container-inner",t.style.right=s+"px",t.style.height=this.headerContainerHeight+"px",e.appendChild(t);var n=this.headerContainer=i.createElement("div");t.appendChild(n),e.addEventListener("click",this.onHeaderActivate.bind(this)),e.addEventListener("wheel",this.onHeaderScroll.bind(this)),this.element.parentNode.insertBefore(e,this.element)},a.prototype.onHeaderActivate=function(e){if(e.target.classList.contains("is-stuck")){for(var t=parseInt(e.target.dataset.index,10),i=this.headers[t],n=0,a=0;t>=a;a++)n+=this.headers[a].height;this.element.scrollTop=i.bottom-n}},a.prototype.onScroll=function(){var e=this.element.scrollTop,t=0;this.headers.forEach(function(i){i.el.parentNode?i.el.parentNode&&i.top>=e&&(this.headerContainer.removeChild(i.el),this.stuckHeadersHeight+=i.height):i.top<=e&&(this.headerContainer.appendChild(i.el),this.stuckHeadersHeight-=i.height),this.isWithinHeaderContainer(i,e)&&(t=i.top-e-this.headerContainerHeight)},this),t+=this.stuckHeadersHeight+this.headerContainerHeight,this.headerContainer.style.transform="translateY("+t+"px)"},a.prototype.onHeaderScroll=function(e){var t=0;switch(e.deltaMode){case WheelEvent.DOM_DELTA_PIXEL:t=e.deltaY;break;case WheelEvent.DOM_DELTA_LINE:t=h*e.deltaY;break;default:t=o}this.element.scrollTop+=t,e.preventDefault()},a.prototype.isWithinHeaderContainer=function(e,t){return e.top>t&&e.top<=this.headerContainerHeight+t};var d="stickies";r.prototype.init=function(){this.akno=new a(this.element,this.options)},r.prototype.destroy=function(){this.akno.destroy(),e.removeData(this.element,"plugin_"+d),this.element=null},e.fn[d]=function(t){var i=arguments,a="plugin_"+d;return t===n||"object"==typeof t?this.each(function(){e.data(this,a)||e.data(this,a,new r(this,t))}):"string"==typeof t&&"_"!==t[0]&&"init"!==t?this.each(function(){var n=e.data(this,a);n instanceof r&&(!e.isFunction(n[t])&&e.isFunction(n.akno[t])&&(n=n.akno),n[t].apply(n,Array.prototype.slice.call(i,1)))}):void 0}}(jQuery,window,window.document); +//# sourceMappingURL=../maps/stickies.jquery.min.js.map \ No newline at end of file diff --git a/dist/scripts/stickies.js b/dist/scripts/stickies.js new file mode 100644 index 0000000..6c06292 --- /dev/null +++ b/dist/scripts/stickies.js @@ -0,0 +1,143 @@ +;(function(window, document, undefined) {/* global WheelEvent */ +'use strict'; + +var SCROLL_WIDTH = (function() { + var scrollDiv = document.createElement('div'); + scrollDiv.className = 'js-scrollbar-measure'; + document.body.appendChild(scrollDiv); + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + return scrollbarWidth; +})(); + +var SCROLL_STEP_DEFAULT = 50; +var LINE_HEIGHT = 16; + +/** + * This plugin enables sticky section headings on a list element. + * + * Supported browsers: Chrome, FF, Safari, IE11+ + * + * @param el - list dom element + * @param options - an option object. Supports: + * - headerSelector - a selector string matching header elements + */ +function StickyHeaders(el, options) { + this.element = el; + this.options = options; + this.stuckHeadersHeight = 0; + + var listOffset = el.getBoundingClientRect().top; + + // this.headers contains clone elements references and cached dimensions for faster scroll handling + this.headers = Array.prototype.map.call(el.querySelectorAll(this.options.headerSelector), (function(header, i) { + var clientRect = header.getBoundingClientRect(); + var clone = header.cloneNode(true); + clone.classList.add('stickies-header', 'is-stuck'); + // explicitly define the height for the clone, just in case it was applied on the original element + // via a selector which is no longer affecting the clone + // + clone.style.height = clientRect.height + 'px'; + // TODO all clones must be of equal height + this.headerContainerHeight = clientRect.height; + + clone.dataset.index = i; + return { + top: clientRect.top - listOffset, + bottom: clientRect.bottom - listOffset, + height: clientRect.height, + el: clone + }; + }).bind(this)); + + this._createHeaderContainer(); + + el.addEventListener('scroll', this.onScroll.bind(this)); +} + +StickyHeaders.prototype._createHeaderContainer = function() { + var header = document.createElement('div'); + header.className = 'stickies-container'; + + var headerWrap = document.createElement('div'); + headerWrap.className = 'stickies-container-inner'; + headerWrap.style.right = SCROLL_WIDTH + 'px'; + headerWrap.style.height = this.headerContainerHeight + 'px'; + header.appendChild(headerWrap); + + var headerContainer = this.headerContainer = document.createElement('div'); + headerWrap.appendChild(headerContainer); + + header.addEventListener('click', this.onHeaderActivate.bind(this)); + header.addEventListener('wheel', this.onHeaderScroll.bind(this)); + + this.element.parentNode.insertBefore(header, this.element); +}; + +StickyHeaders.prototype.onHeaderActivate = function(ev) { + if (ev.target.classList.contains('is-stuck')) { + var index = parseInt(ev.target.dataset.index, 10); + var targetHeader = this.headers[index]; + var scrollOffset = 0; + for (var i = 0; i <= index; i++) { + scrollOffset += this.headers[i].height; + } + + // Scrollable area is reduces by the height of stuck headers. + // Need to account for that when jumping to the new position. + this.element.scrollTop = targetHeader.bottom - scrollOffset; + } +}; + +StickyHeaders.prototype.onScroll = function() { + var scrollTop = this.element.scrollTop; + var shiftAmount = 0; + + this.headers.forEach(function(header) { + if (!header.el.parentNode) { + if (header.top <= scrollTop) { + this.headerContainer.appendChild(header.el); + this.stuckHeadersHeight -= header.height; + } + } else if (header.el.parentNode) { + if (header.top >= scrollTop) { + this.headerContainer.removeChild(header.el); + this.stuckHeadersHeight += header.height; + } + } + + if (this.isWithinHeaderContainer(header, scrollTop)) { + // the distance between the top of the scrollable area and the header top + // minus the height of the header container gives the shift amount + // for the stuck headers on in order to have an effect of a 'replacement' + // of the old header with a new one + shiftAmount = (header.top - scrollTop) - this.headerContainerHeight; + } + }, this); + + shiftAmount += this.stuckHeadersHeight + this.headerContainerHeight; + + this.headerContainer.style.transform = 'translateY(' + shiftAmount + 'px)'; +}; + +StickyHeaders.prototype.onHeaderScroll = function(ev) { + var scrollDelta = 0; + switch (ev.deltaMode) { + case WheelEvent.DOM_DELTA_PIXEL: + scrollDelta = ev.deltaY; + break; + case WheelEvent.DOM_DELTA_LINE: + scrollDelta = LINE_HEIGHT * ev.deltaY; + break; + default: + scrollDelta = SCROLL_STEP_DEFAULT; + } + this.element.scrollTop += scrollDelta; + // prevent the viewport from scrolling + ev.preventDefault(); +}; + +StickyHeaders.prototype.isWithinHeaderContainer = function(header, scrollTop) { + return header.top > scrollTop && header.top <= this.headerContainerHeight + scrollTop; +}; +})(window, window.document); \ No newline at end of file diff --git a/dist/scripts/stickies.min.js b/dist/scripts/stickies.min.js new file mode 100644 index 0000000..49a9d4b --- /dev/null +++ b/dist/scripts/stickies.min.js @@ -0,0 +1,2 @@ +!function(e,t){"use strict";function i(e,t){this.element=e,this.options=t,this.stuckHeadersHeight=0;var i=e.getBoundingClientRect().top;this.headers=Array.prototype.map.call(e.querySelectorAll(this.options.headerSelector),function(e,t){var n=e.getBoundingClientRect(),r=e.cloneNode(!0);return r.classList.add("stickies-header","is-stuck"),r.style.height=n.height+"px",this.headerContainerHeight=n.height,r.dataset.index=t,{top:n.top-i,bottom:n.bottom-i,height:n.height,el:r}}.bind(this)),this._createHeaderContainer(),e.addEventListener("scroll",this.onScroll.bind(this))}var n=function(){var e=t.createElement("div");e.className="js-scrollbar-measure",t.body.appendChild(e);var i=e.offsetWidth-e.clientWidth;return t.body.removeChild(e),i}(),r=50,a=16;i.prototype._createHeaderContainer=function(){var e=t.createElement("div");e.className="stickies-container";var i=t.createElement("div");i.className="stickies-container-inner",i.style.right=n+"px",i.style.height=this.headerContainerHeight+"px",e.appendChild(i);var r=this.headerContainer=t.createElement("div");i.appendChild(r),e.addEventListener("click",this.onHeaderActivate.bind(this)),e.addEventListener("wheel",this.onHeaderScroll.bind(this)),this.element.parentNode.insertBefore(e,this.element)},i.prototype.onHeaderActivate=function(e){if(e.target.classList.contains("is-stuck")){for(var t=parseInt(e.target.dataset.index,10),i=this.headers[t],n=0,r=0;t>=r;r++)n+=this.headers[r].height;this.element.scrollTop=i.bottom-n}},i.prototype.onScroll=function(){var e=this.element.scrollTop,t=0;this.headers.forEach(function(i){i.el.parentNode?i.el.parentNode&&i.top>=e&&(this.headerContainer.removeChild(i.el),this.stuckHeadersHeight+=i.height):i.top<=e&&(this.headerContainer.appendChild(i.el),this.stuckHeadersHeight-=i.height),this.isWithinHeaderContainer(i,e)&&(t=i.top-e-this.headerContainerHeight)},this),t+=this.stuckHeadersHeight+this.headerContainerHeight,this.headerContainer.style.transform="translateY("+t+"px)"},i.prototype.onHeaderScroll=function(e){var t=0;switch(e.deltaMode){case WheelEvent.DOM_DELTA_PIXEL:t=e.deltaY;break;case WheelEvent.DOM_DELTA_LINE:t=a*e.deltaY;break;default:t=r}this.element.scrollTop+=t,e.preventDefault()},i.prototype.isWithinHeaderContainer=function(e,t){return e.top>t&&e.top<=this.headerContainerHeight+t}}(window,window.document); +//# sourceMappingURL=../maps/stickies.min.js.map \ No newline at end of file diff --git a/dist/styles/stickies.css b/dist/styles/stickies.css new file mode 100644 index 0000000..07ce6f6 --- /dev/null +++ b/dist/styles/stickies.css @@ -0,0 +1,22 @@ +.stickies-container { + position: relative; } + +.stickies-container-inner { + overflow: hidden; + position: absolute; + top: 1px; + left: 1px; } + +.stickies-header.is-stuck { + box-sizing: border-box; + cursor: pointer; } + +.js-scrollbar-measure { + width: 100px; + height: 100px; + overflow: scroll; + position: absolute; + top: -9999px; } + + +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uc2NzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtFQUNZLG9CQUFBLEVBQUE7O0FBSVo7RUFDSSxrQkFBVTtFQUNWLG9CQUFVO0VBR1YsVUFBSztFQUNMLFdBQU0sRUFBQTs7QUFJVjtFQUNFLHdCQUFZO0VBQ0osaUJBQUEsRUFBQTs7QUFJVjtFQUNFLGNBQU87RUFDUCxlQUFRO0VBQ1Isa0JBQVU7RUFDVixvQkFBVTtFQUNWLGNBQUssRUFBQSIsImZpbGUiOiJtYWluLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi5zdGlja2llcy1jb250YWluZXIge1xuICBwb3NpdGlvbjogcmVsYXRpdmU7XG59XG5cblxuICAuc3RpY2tpZXMtY29udGFpbmVyLWlubmVyIHtcbiAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAvLyBjb21wZW5zYXRlIGxpc3QncyBib3JkZXJcbiAgICAvLyBUT0RPIHB1dCBpbiBKU1xuICAgIHRvcDogMXB4O1xuICAgIGxlZnQ6IDFweDtcbiAgfVxuXG5cbi5zdGlja2llcy1oZWFkZXIuaXMtc3R1Y2sge1xuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xuICBjdXJzb3I6IHBvaW50ZXI7XG59XG5cblxuLmpzLXNjcm9sbGJhci1tZWFzdXJlIHtcbiAgd2lkdGg6IDEwMHB4O1xuICBoZWlnaHQ6IDEwMHB4O1xuICBvdmVyZmxvdzogc2Nyb2xsO1xuICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIHRvcDogLTk5OTlweDtcbn1cbiJdLCJzb3VyY2VSb290IjoiL3NvdXJjZS8ifQ== */ \ No newline at end of file diff --git a/dist/styles/stickies.min.css b/dist/styles/stickies.min.css new file mode 100644 index 0000000..821b48d --- /dev/null +++ b/dist/styles/stickies.min.css @@ -0,0 +1 @@ +.stickies-container{position:relative}.stickies-container-inner{overflow:hidden;position:absolute;top:1px;left:1px}.stickies-header.is-stuck{box-sizing:border-box;cursor:pointer}.js-scrollbar-measure{width:100px;height:100px;overflow:scroll;position:absolute;top:-9999px} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 2241863..83deb54 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,11 +1,18 @@ /*global -$ */ 'use strict'; +var DIR_BUILD_BASE = 'dist/'; +var DIR_BUILD_SCRIPTS = DIR_BUILD_BASE + 'scripts/'; +var DIR_BUILD_STYLES = DIR_BUILD_BASE + 'styles/'; + + var gulp = require('gulp'); var plugins = require('gulp-load-plugins')(); var browserSync = require('browser-sync'); var reload = browserSync.reload; + + function processStyles (src) { return function () { return gulp.src(src) @@ -62,15 +69,6 @@ gulp.task('html', ['styles'], function() { .pipe(gulp.dest('dist')); }); -gulp.task('extras', function() { - return gulp.src([ - 'demo/*.*', - '!demo/*.html' - ], { - dot: true - }).pipe(gulp.dest('dist')); -}); - gulp.task('clean', require('del').bind(null, ['.tmp', 'dist'])); gulp.task('serve', ['styles', 'styles-demo'], function() { @@ -100,8 +98,53 @@ gulp.task('serve', ['styles', 'styles-demo'], function() { ], ['styles-demo']); }); -gulp.task('build', ['jshint', 'styles', 'extras'], function() { - return gulp.src('dist/**/*').pipe(plugins.size({ + +gulp.task('build:vanilla', ['jshint'], function() { + + return gulp.src('src/scripts/main.js') + .pipe(plugins.sourcemaps.init()) + .pipe(plugins.header(';(function(window, document, undefined) {')) + .pipe(plugins.footer('})(window, window.document);')) + .pipe(plugins.rename({ basename: 'stickies' })) + .pipe(plugins.size({ showFiles: true })) + .pipe(gulp.dest(DIR_BUILD_SCRIPTS)) + .pipe(plugins.uglify()) + .pipe(plugins.rename({ extname: '.min.js' })) + .pipe(plugins.size({ showFiles: true })) + .pipe(plugins.sourcemaps.write('../maps')) + .pipe(gulp.dest(DIR_BUILD_SCRIPTS)); + +}); + +gulp.task('build:jquery', ['jshint'], function() { + + return gulp.src(['src/scripts/main.js', 'src/scripts/jquery-plugin.js']) + .pipe(plugins.sourcemaps.init()) + .pipe(plugins.concat('stickies.jquery.js')) + .pipe(plugins.header(';(function($, window, document, undefined) {')) + .pipe(plugins.footer('})(jQuery, window, window.document);')) + .pipe(plugins.size({ showFiles: true })) + .pipe(gulp.dest(DIR_BUILD_SCRIPTS)) + .pipe(plugins.uglify()) + .pipe(plugins.rename({ extname: '.min.js' })) + .pipe(plugins.size({ showFiles: true })) + .pipe(plugins.sourcemaps.write('../maps')) + .pipe(gulp.dest(DIR_BUILD_SCRIPTS)); +}); + +gulp.task('build:styles', ['styles'], function() { + + return gulp.src(['.tmp/styles/main.css']) + .pipe(plugins.rename({ basename: 'stickies' })) + .pipe(gulp.dest(DIR_BUILD_STYLES)) + .pipe(plugins.csso()) + .pipe(plugins.rename({ extname: '.min.css' })) + .pipe(gulp.dest(DIR_BUILD_STYLES)); +}); + +gulp.task('build', ['build:vanilla', 'build:jquery', 'build:styles'], function() { + + return gulp.src(DIR_BUILD_BASE + '**/*').pipe(plugins.size({ title: 'build', gzip: true })); diff --git a/package.json b/package.json index ca592af..0e2d5f5 100644 --- a/package.json +++ b/package.json @@ -9,17 +9,20 @@ "del": "^1.1.1", "gulp": "^3.6.0", "gulp-cache": "^0.2.2", + "gulp-concat": "^2.5.2", "gulp-csso": "^0.2.6", + "gulp-footer": "^1.0.5", + "gulp-header": "^1.2.2", "gulp-if": "^1.2.1", - "gulp-imagemin": "^2.0.0", "gulp-jshint": "^1.5.3", "gulp-load-plugins": "^0.8.0", "gulp-minify-html": "^0.1.6", "gulp-postcss": "^3.0.0", + "gulp-rename": "^1.2.0", "gulp-sass": "^1.3.3", "gulp-size": "^1.1.0", - "gulp-sourcemaps": "^1.3.0", - "gulp-uglify": "^1.0.1", + "gulp-sourcemaps": "^1.5.1", + "gulp-uglify": "^1.1.0", "gulp-useref": "^1.0.2", "jshint-stylish": "^1.0.0", "main-bower-files": "^2.5.0", diff --git a/src/scripts/jquery-plugin.js b/src/scripts/jquery-plugin.js new file mode 100644 index 0000000..fb2e396 --- /dev/null +++ b/src/scripts/jquery-plugin.js @@ -0,0 +1,40 @@ +var pluginName = 'stickies'; + +function Plugin(element, options) { + this.element = element; + this.options = options; + this.init(); +} + +Plugin.prototype.init = function() { + this.akno = new StickyHeaders(this.element, this.options); +}; + +Plugin.prototype.destroy = function() { + this.akno.destroy(); + $.removeData(this.element, 'plugin_' + pluginName); + this.element = null; +}; + +$.fn[pluginName] = function(options) { + var args = arguments; + var dataKey = 'plugin_' + pluginName; + if (options === undefined || typeof options === 'object') { + return this.each(function() { + if (!$.data(this, dataKey)) { + $.data(this, dataKey, new Plugin(this, options)); + } + }); + } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { + return this.each(function() { + var instance = $.data(this, dataKey); + if (instance instanceof Plugin) { + // call with the widget instance if not on the plugin + if(!$.isFunction(instance[options]) && $.isFunction(instance.akno[options])) { + instance = instance.akno; + } + instance[options].apply(instance, Array.prototype.slice.call(args, 1)); + } + }); + } +};