audero-sticky
Advanced tools
Comparing version 0.1.2 to 0.2.0
{ | ||
"name": "audero-sticky", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "Audero Sticky is a polyfill for the CSS position: sticky with no dependencies and support for multiple module systems.", | ||
@@ -5,0 +5,0 @@ "main": "src/audero-sticky.js", |
@@ -1,3 +0,3 @@ | ||
/*! audero-sticky.js 0.1.2 | Aurelio De Rosa (@AurelioDeRosa) | MIT/GPL-3.0 Licensed */ | ||
!function(a,b){"use strict";"function"==typeof define&&define.amd?define(b):"object"==typeof module&&module.exports?module.exports=b():a.Sticky=b()}(this,function(){"use strict";function a(a,b,c){var d=document.createEvent("Event");d.initEvent(b,!0,!0);for(var e in c)c.hasOwnProperty(e)&&(d[e]=c[e]);a.dispatchEvent(d)}function b(a,b){b.forEach(function(b){a[b]=""})}function c(a,b,c){c||(c=Object.keys(b)),c.forEach(function(c){a[c]=b[c]})}function d(a,b){var c=[].slice.call(document.querySelectorAll(b));return c.indexOf(a)+1}function e(a){var b={};for(var c in a)b[c]=a[c]+"px";return b}function f(a){b(a.element.style,m.concat(["marginTop","marginBottom"])),a.element.style.position=a._position,a._placeholder&&a._placeholder.parentNode&&a._placeholder.parentNode.removeChild(a._placeholder)}function g(a){var b={},c=window.getComputedStyle(a),d=a.parentNode.getBoundingClientRect();return"auto"!==c.top?(b.start=a.getBoundingClientRect().top-parseFloat(c.top),b.end=d.bottom):(b.start=a.getBoundingClientRect().bottom+parseFloat(c.bottom),b.end=d.top),b.start+=window.pageYOffset,b.end+=window.pageYOffset,b}function h(b){function d(){b._position=b.element.style.position,c(b.element.style,{position:"fixed",marginTop:0,marginBottom:0}),c(b.element.style,b._placeholder.style,m),b.element.parentNode.insertBefore(b._placeholder,b.element),j=!0,a(b.element,"stickystart")}function e(){f(b),j=!1,a(b.element,"stickyend")}function h(){k=g(j?b._placeholder:b.element);var a=~~parseFloat(window.getComputedStyle(b.element).height),c=k.end-a-window.pageYOffset,f=window.pageYOffset>=k.start&&window.pageYOffset<=k.end;f?(j||d(),b.element.style.top=c-n>=0?"":c+"px"):j&&e()}function i(){k=g(j?b._placeholder:b.element);var a=~~parseFloat(window.getComputedStyle(b.element).height),c=window.pageYOffset+window.innerHeight,f=k.end+a-c,h=c<=k.start&&c>=k.end;h?(j||d(),b.element.style.bottom=0>=f+n?"":-f+"px"):j&&e()}var j=!1,k=g(b.element),l=window.getComputedStyle(b.element),n="auto"!==l.top?parseFloat(l.top):parseFloat(l.bottom);return"auto"!==l.top?h:i}function i(a){return function(){window.removeEventListener("resize",a._handlers.resize),a.destroy(),a.init(),a._handlers.scroll()}}function j(a){window.addEventListener("load",a._handlers.scroll),window.addEventListener("scroll",a._handlers.scroll),window.addEventListener("resize",a._handlers.resize)}function k(a,b){this.element=a,this.settings=b||l,this._placeholder=null,this._position="",this._handlers={}}var l={selector:".sticky"},m=["width","height","left","marginLeft","marginRight","zIndex"];return k.isFeatureSupported=function(){var a=document.createElement("div");return a.style.cssText="position:sticky",!!a.style.position},k.autoInit=function(a){a=a||l,[].forEach.call(document.querySelectorAll(a.selector),function(b){var c=new k(b,a);c.init()})},k.prototype.init=function(){var a=this.element.getBoundingClientRect();this._placeholder=document.createElement("div"),this._handlers.scroll=h(this),this._handlers.resize=i(this),this._placeholder.style.zIndex=d(this.element,this.settings.selector),c(this._placeholder.style,window.getComputedStyle(this.element),["top","bottom","marginTop","marginBottom","marginLeft","marginRight"]),c(this._placeholder.style,e(a),["width","height","left"]),j(this)},k.prototype.destroy=function(){f(this),window.removeEventListener("scroll",this._handlers.scroll),window.removeEventListener("resize",this._handlers.resize),this._handlers={},this._position="",this._placeholder=null},k}); | ||
/*! audero-sticky.js 0.2.0 | Aurelio De Rosa (@AurelioDeRosa) | MIT/GPL-3.0 Licensed */ | ||
!function(a,b){"use strict";"function"==typeof define&&define.amd?define(b):"object"==typeof module&&module.exports?module.exports=b():a.Sticky=b()}(this,function(){"use strict";function a(a,b,c){var d=document.createEvent("Event");d.initEvent(b,!0,!0);for(var e in c)c.hasOwnProperty(e)&&(d[e]=c[e]);a.dispatchEvent(d)}function b(a){var b={};a=a||{};for(var c in q)q.hasOwnProperty(c)&&(b[c]=a[c]||q[c]);return b}function c(a,b){var c=new RegExp("\\b"+b+"\\b");c.test(a.className)||(a.className+=" "+b)}function d(a,b){var c=new RegExp("\\b"+b+"\\b");a.className=a.className.replace(c,"").trim()}function e(a){var b,e;return c(a.element,a.settings.activeClass),b=window.getComputedStyle(a.element),e={marginBottom:b.marginBottom,marginTop:b.marginTop},d(a.element,a.settings.activeClass),e}function f(a,b){b.forEach(function(b){a[b]=""})}function g(a,b,c){c||(c=Object.keys(b)),c.forEach(function(c){a[c]=b[c]})}function h(a,b){var c=[].slice.call(document.querySelectorAll(b));return c.indexOf(a)+1}function i(a){var b={};for(var c in a)b[c]=a[c]+"px";return b}function j(a){f(a.element.style,r.concat(["marginTop","marginBottom"])),a.element.style.position=a._position,a._placeholder&&a._placeholder.parentNode&&a._placeholder.parentNode.removeChild(a._placeholder)}function k(a,b){var c={},d=window.getComputedStyle(a),e=a.parentNode.getBoundingClientRect();return"auto"!==d.top?(c.start=a.getBoundingClientRect().top-parseFloat(d.top),c.end=e.bottom-~~parseFloat(b.marginBottom)):(c.start=a.getBoundingClientRect().bottom+parseFloat(d.bottom),c.end=e.top+~~parseFloat(b.marginTop)),c.start+=window.pageYOffset,c.end+=window.pageYOffset,c}function l(a){var b=a.element.getBoundingClientRect();g(a._placeholder.style,window.getComputedStyle(a.element),["top","bottom","marginTop","marginBottom","marginLeft","marginRight"]),g(a._placeholder.style,i(b),["width","height","left"])}function m(b){function f(){l(b),b._position=b.element.style.position,g(b.element.style,{position:"fixed"}),g(b.element.style,b._placeholder.style,r),b.element.parentNode.insertBefore(b._placeholder,b.element),n=!0,a(b.element,"stickystart"),c(b.element,b.settings.activeClass)}function h(){j(b),n=!1,a(b.element,"stickyend"),d(b.element,b.settings.activeClass)}function i(){p=n?k(b._placeholder,o):k(b.element,o);var a=~~parseFloat(window.getComputedStyle(b.element).height),c=p.end-a-window.pageYOffset,d=window.pageYOffset>=p.start&&window.pageYOffset<=p.end;d?(n||f(),b.element.style.top=c-s>=0?"":c+"px"):n&&h()}function m(){p=n?k(b._placeholder,o):k(b.element,o);var a=~~parseFloat(window.getComputedStyle(b.element).height),c=window.pageYOffset+window.innerHeight,d=p.end+a-c,e=c<=p.start&&c>=p.end;e?(n||f(),b.element.style.bottom=0>=d+s?"":-d+"px"):n&&h()}var n=!1,o=e(b),p=k(b.element,o),q=window.getComputedStyle(b.element),s="auto"!==q.top?parseFloat(q.top):parseFloat(q.bottom);return"auto"!==q.top?i:m}function n(a){return function(){window.removeEventListener("resize",a._handlers.resize),a.destroy(),a.init(),a._handlers.scroll()}}function o(a){window.addEventListener("load",a._handlers.scroll),window.addEventListener("scroll",a._handlers.scroll),window.addEventListener("resize",a._handlers.resize)}function p(a,c){this.element=a,this.settings=b(c),this._placeholder=null,this._position="",this._handlers={}}var q={selector:".sticky",activeClass:"sticky--active"},r=["width","height","left","marginLeft","marginRight","zIndex"];return p.isFeatureSupported=function(){var a=document.createElement("div");return a.style.cssText="position:sticky",!!a.style.position},p.autoInit=function(a){a=a||q,[].forEach.call(document.querySelectorAll(a.selector),function(b){var c=new p(b,a);c.init()})},p.prototype.init=function(){var a=this.element.getBoundingClientRect();this._placeholder=document.createElement("div"),this._handlers.scroll=m(this),this._handlers.resize=n(this),this._placeholder.style.zIndex=h(this.element,this.settings.selector),g(this._placeholder.style,window.getComputedStyle(this.element),["top","bottom","marginTop","marginBottom","marginLeft","marginRight"]),g(this._placeholder.style,i(a),["width","height","left"]),o(this)},p.prototype.destroy=function(){j(this),window.removeEventListener("scroll",this._handlers.scroll),window.removeEventListener("resize",this._handlers.resize),this._handlers={},this._position="",this._placeholder=null},p}); | ||
//# sourceMappingURL=audero-sticky.min.js.map |
{ | ||
"name": "audero-sticky", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "Audero Sticky is a polyfill for the CSS position: sticky with no dependencies and support for multiple module systems.", | ||
@@ -5,0 +5,0 @@ "main": "src/audero-sticky.js", |
@@ -13,2 +13,3 @@ # Audero Sticky | ||
- **Support multiple module systems**: It can be used with Browserify, RequireJS, and in the browser | ||
- **Hooks available**: Triggers events and adds a class name when an element starts and ends sticking | ||
@@ -113,3 +114,3 @@ ## Demo | ||
Sticky.js provides the methods described in the following sections. | ||
Audero Sticky provides the methods described in the following sections. | ||
@@ -138,2 +139,10 @@ ### `Sticky.isFeatureSupported()` | ||
## Events | ||
Audero Sticky triggers a few events on the element(s) that will stick. You can listen to these events to perform custom | ||
actions when needed: | ||
* `stickystart`: Triggered when the element starts sticking | ||
* `stickyend`: Triggered when the element ends sticking | ||
## Options | ||
@@ -144,2 +153,3 @@ | ||
* `selector` (`string`. Default: `'.sticky'`): The selector used to identify the elements processed by this library | ||
* `activeClass` (`string`. Default: `'sticky-active'`): The class name added when an element starts sticking | ||
@@ -146,0 +156,0 @@ ## Examples |
@@ -19,2 +19,4 @@ (function(root, factory) { | ||
* elements processed by this library | ||
* @property {string} [activeClass='sticky-active'] The class name added when an | ||
* element starts sticking | ||
*/ | ||
@@ -28,3 +30,4 @@ | ||
var defaults = { | ||
selector: '.sticky' | ||
selector: '.sticky', | ||
activeClass: 'sticky--active' | ||
}; | ||
@@ -69,2 +72,78 @@ | ||
/** | ||
* Merge the settings provided with the default values. | ||
* The method does not modify the object provided. | ||
* | ||
* @param {Object} [settings={}] The settings to merge | ||
* | ||
* @returns {Object} | ||
*/ | ||
function mergeSettings(settings) { | ||
var mergedObject = {}; | ||
settings = settings || {}; | ||
for(var property in defaults) { | ||
if (!defaults.hasOwnProperty(property)) { | ||
continue; | ||
} | ||
mergedObject[property] = settings[property] || defaults[property]; | ||
} | ||
return mergedObject; | ||
} | ||
/** | ||
* Adds a class name to an element | ||
* | ||
* @param {HTMLElement} element The element on which the class name is added | ||
* @param {string} className The class name to add | ||
*/ | ||
function addClass(element, className) { | ||
var regex = new RegExp('\\b' + className + '\\b'); | ||
if (!regex.test(element.className)) { | ||
element.className += ' ' + className; | ||
} | ||
} | ||
/** | ||
* Removes a class name from an element | ||
* | ||
* @param {HTMLElement} element The element on which the class name is removed | ||
* @param {string} className The class name to remove | ||
*/ | ||
function removeClass(element, className) { | ||
var regex = new RegExp('\\b' + className + '\\b'); | ||
element.className = element.className.replace(regex, '').trim(); | ||
} | ||
/** | ||
* Calculates the top and bottom margins of the element that has to stick | ||
* at the moment it'll stick | ||
* | ||
* @param {Sticky} sticky An instance of a Sticky object | ||
* | ||
* @returns {Object} | ||
*/ | ||
function getStickyMargins(sticky) { | ||
// Knowing the top and bottom margins at the time the element | ||
// will stick is important because the specifications require | ||
// to consider these values when calculating the boundaries | ||
// in which the element sticks. | ||
var elementStyle, stickyMargins; | ||
addClass(sticky.element, sticky.settings.activeClass); | ||
elementStyle = window.getComputedStyle(sticky.element); | ||
stickyMargins = { | ||
marginBottom: elementStyle.marginBottom, | ||
marginTop: elementStyle.marginTop | ||
}; | ||
removeClass(sticky.element, sticky.settings.activeClass); | ||
return stickyMargins; | ||
} | ||
/** | ||
* Resets the style of the properties specifies | ||
@@ -157,6 +236,8 @@ * | ||
* @param {HTMLElement} element The element based on which the boundaries are calculated | ||
* @param {Object} stickyMargins An object containing additional margins to consider | ||
* in the calculation | ||
* | ||
* @return {Object} | ||
*/ | ||
function calculateBoundaries(element) { | ||
function calculateBoundaries(element, stickyMargins) { | ||
var boundaries = {}; | ||
@@ -170,6 +251,6 @@ var elementStyle = window.getComputedStyle(element); | ||
boundaries.start = element.getBoundingClientRect().top - parseFloat(elementStyle.top); | ||
boundaries.end = parentStyle.bottom; | ||
boundaries.end = parentStyle.bottom - ~~parseFloat(stickyMargins.marginBottom); | ||
} else { | ||
boundaries.start = element.getBoundingClientRect().bottom + parseFloat(elementStyle.bottom); | ||
boundaries.end = parentStyle.top; | ||
boundaries.end = parentStyle.top + ~~parseFloat(stickyMargins.marginTop); | ||
} | ||
@@ -187,2 +268,34 @@ | ||
/** | ||
* Updates the style of the placeholder element based on the current | ||
* values of the sticky element | ||
* | ||
* @param {Sticky} sticky An instance of a Sticky object | ||
*/ | ||
function updatePlaceholderStyle(sticky) { | ||
var startPosition = sticky.element.getBoundingClientRect(); | ||
copyStyleProperties( | ||
sticky._placeholder.style, | ||
window.getComputedStyle(sticky.element), | ||
[ | ||
'top', | ||
'bottom', | ||
'marginTop', | ||
'marginBottom', | ||
'marginLeft', | ||
'marginRight' | ||
] | ||
); | ||
copyStyleProperties( | ||
sticky._placeholder.style, | ||
convertNumbersToPixels(startPosition), | ||
[ | ||
'width', | ||
'height', | ||
'left' | ||
] | ||
); | ||
} | ||
/** | ||
* Returns the function to use as the event handler for | ||
@@ -197,3 +310,4 @@ * the <code>scroll</code> event | ||
var isAdded = false; | ||
var boundaries = calculateBoundaries(sticky.element); | ||
var stickyMargins = getStickyMargins(sticky); | ||
var boundaries = calculateBoundaries(sticky.element, stickyMargins); | ||
var elementStyle = window.getComputedStyle(sticky.element); | ||
@@ -205,2 +319,3 @@ var distanceFromSide = elementStyle.top !== 'auto' ? | ||
function startSticky() { | ||
updatePlaceholderStyle(sticky); | ||
sticky._position = sticky.element.style.position; | ||
@@ -210,5 +325,3 @@ copyStyleProperties( | ||
{ | ||
position: 'fixed', | ||
marginTop: 0, | ||
marginBottom: 0 | ||
position: 'fixed' | ||
} | ||
@@ -220,2 +333,3 @@ ); | ||
triggerEvent(sticky.element, 'stickystart'); | ||
addClass(sticky.element, sticky.settings.activeClass); | ||
} | ||
@@ -227,8 +341,11 @@ | ||
triggerEvent(sticky.element, 'stickyend'); | ||
removeClass(sticky.element, sticky.settings.activeClass); | ||
} | ||
function stickToTop() { | ||
// The boundaries are calculated based on the element itself if it's not stickying; | ||
// The boundaries are calculated based on the element itself if it's not sticking; | ||
// otherwise the placeholder is used. | ||
boundaries = isAdded ? calculateBoundaries(sticky._placeholder) : calculateBoundaries(sticky.element); | ||
boundaries = isAdded ? | ||
calculateBoundaries(sticky._placeholder, stickyMargins) : | ||
calculateBoundaries(sticky.element, stickyMargins); | ||
@@ -252,5 +369,7 @@ // Same as value || 0 | ||
function stickToBottom() { | ||
// The boundaries are calculated based on the element itself if it's not stickying; | ||
// The boundaries are calculated based on the element itself if it's not sticking; | ||
// otherwise the placeholder is used. | ||
boundaries = isAdded ? calculateBoundaries(sticky._placeholder) : calculateBoundaries(sticky.element); | ||
boundaries = isAdded ? | ||
calculateBoundaries(sticky._placeholder, stickyMargins) : | ||
calculateBoundaries(sticky.element, stickyMargins); | ||
@@ -315,3 +434,3 @@ // Same as value || 0 | ||
this.element = element; | ||
this.settings = options || defaults; | ||
this.settings = mergeSettings(options); | ||
this._placeholder = null; | ||
@@ -318,0 +437,0 @@ this._position = ''; |
Sorry, the diff of this file is not supported yet
92522
559
222