UNPKG

headhesive

Version:

An on-demand sticky header

150 lines 4.95 kB
/*! * Headhesive.js v1.2.3 - An on-demand sticky header * Author: Copyright (c) Mark Goodyear <@markgdyr> <http://markgoodyear.com> * Url: http://markgoodyear.com/labs/headhesive * License: MIT */ (function(root, factory) { if (typeof define === "function" && define.amd) { define([], function() { return factory(); }); } else if (typeof exports === "object") { module.exports = factory(); } else { root.Headhesive = factory(); } })(this, function() { "use strict"; var _mergeObj = function(to, from) { for (var p in from) { if (from.hasOwnProperty(p)) { to[p] = typeof from[p] === "object" ? _mergeObj(to[p], from[p]) : from[p]; } } return to; }; var _throttle = function(func, wait) { var _now = Date.now || function() { return new Date().getTime(); }; var context, args, result; var timeout = null; var previous = 0; var later = function() { previous = _now(); timeout = null; result = func.apply(context, args); context = args = null; }; return function() { var now = _now(); var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); context = args = null; } else if (!timeout) { timeout = setTimeout(later, remaining); } return result; }; }; var _getScrollY = function() { return window.pageYOffset !== undefined ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; }; var _getElemY = function(elem, side) { var pos = 0; var elemHeight = elem.offsetHeight; while (elem) { pos += elem.offsetTop; elem = elem.offsetParent; } if (side === "bottom") { pos = pos + elemHeight; } return pos; }; var Headhesive = function(elem, options) { if (!("querySelector" in document && "addEventListener" in window)) { return; } this.visible = false; this.options = { offset: 300, offsetSide: "top", classes: { clone: "headhesive", stick: "headhesive--stick", unstick: "headhesive--unstick" }, throttle: 250, onInit: function() {}, onStick: function() {}, onUnstick: function() {}, onDestroy: function() {} }; this.elem = typeof elem === "string" ? document.querySelector(elem) : elem; this.options = _mergeObj(this.options, options); this.init(); }; Headhesive.prototype = { constructor: Headhesive, init: function() { this.clonedElem = this.elem.cloneNode(true); this.clonedElem.className += " " + this.options.classes.clone; document.body.insertBefore(this.clonedElem, document.body.firstChild); if (typeof this.options.offset === "number") { this.scrollOffset = this.options.offset; } else if (typeof this.options.offset === "string") { this._setScrollOffset(); } else { throw new Error("Invalid offset: " + this.options.offset); } this._throttleUpdate = _throttle(this.update.bind(this), this.options.throttle); this._throttleScrollOffset = _throttle(this._setScrollOffset.bind(this), this.options.throttle); window.addEventListener("scroll", this._throttleUpdate, false); window.addEventListener("resize", this._throttleScrollOffset, false); this.options.onInit.call(this); }, _setScrollOffset: function() { if (typeof this.options.offset === "string") { this.scrollOffset = _getElemY(document.querySelector(this.options.offset), this.options.offsetSide); } }, destroy: function() { document.body.removeChild(this.clonedElem); window.removeEventListener("scroll", this._throttleUpdate); window.removeEventListener("resize", this._throttleScrollOffset); this.options.onDestroy.call(this); }, stick: function() { if (!this.visible) { this.clonedElem.className = this.clonedElem.className.replace(new RegExp("(^|\\s)*" + this.options.classes.unstick + "(\\s|$)*", "g"), ""); this.clonedElem.className += " " + this.options.classes.stick; this.visible = true; this.options.onStick.call(this); } }, unstick: function() { if (this.visible) { this.clonedElem.className = this.clonedElem.className.replace(new RegExp("(^|\\s)*" + this.options.classes.stick + "(\\s|$)*", "g"), ""); this.clonedElem.className += " " + this.options.classes.unstick; this.visible = false; this.options.onUnstick.call(this); } }, update: function() { if (_getScrollY() > this.scrollOffset) { this.stick(); } else { this.unstick(); } } }; return Headhesive; });