formstone
Version:
Library of modular front end components.
402 lines (320 loc) • 9.19 kB
JavaScript
/* global define */
(function(factory) {
if (typeof define === "function" && define.amd) {
define([
"jquery",
"./core",
"./mediaquery",
], factory);
} else {
factory(jQuery, Formstone);
}
}(function($, Formstone) {
"use strict";
/**
* @method private
* @name resize
* @description Handles window resize
*/
function resize() {
Functions.iterate.call($Instances, resizeInstance);
}
/**
* @method private
* @name raf
* @description Handles request animation frame
*/
function raf() {
ScrollTop = $Window.scrollTop();
if (ScrollTop < 0) {
ScrollTop = 0;
}
if (ScrollTop !== OldScrollTop) {
renderRAF();
OldScrollTop = ScrollTop;
}
}
/**
* @method private
* @name cacheInstances
* @description Caches active instances
*/
function cacheInstances() {
$Instances = $(Classes.base);
resize();
}
/**
* @method private
* @name construct
* @description Builds instance.
* @param data [object] "Instance data"
*/
function construct(data) {
data.enabled = false;
data.stuck = false;
data.passed = true;
data.$clone = data.$el.clone();
data.container = data.$el.data("sticky-container");
data.$container = $(data.container);
data.$el.addClass(RawClasses.base);
data.$clone.removeClass(RawClasses.element)
.addClass(RawClasses.clone);
data.$container.addClass(RawClasses.container);
data.$stickys = $().add(data.$el).add(data.$clone);
data.$el.after(data.$clone);
var $images = $().add( data.$el.find("img") ).add( data.$container.find("img") );
if ($images.length) {
$images.on(Events.load, data, resizeInstance);
}
data.maxWidth = (data.maxWidth === Infinity ? "100000px" : data.maxWidth);
data.mq = "(min-width:" + data.minWidth + ") and (max-width:" + data.maxWidth + ")";
// Media Query support
$.fsMediaquery("bind", data.rawGuid, data.mq, {
enter: function() {
enable.call(data.$el, data);
},
leave: function() {
disable.call(data.$el, data);
}
});
}
/**
* @method private
* @name postConstruct
* @description Run post build.
* @param data [object] "Instance data"
*/
function postConstruct(data) {
cacheInstances();
resize();
}
/**
* @method private
* @name destruct
* @description Tears down instance.
* @param data [object] "Instance data"
*/
function destruct(data) {
data.$clone.remove();
data.$container.removeClass(RawClasses.container);
data.$el.css({
height: '',
width: '',
top: '',
bottom: '',
marginBottom: ''
}).removeClass(RawClasses.base);
cacheInstances();
}
/**
* @method
* @name enable
* @description Enables instance.
* @example $(".target").sticky("enable");
*/
function enable(data) {
data.enabled = true;
data.$el.addClass(RawClasses.enabled);
resizeInstance(data);
}
/**
* @method
* @name disable
* @description Disables instance.
* @example $(".target").sticky("disable");
*/
function disable(data) {
data.enabled = false;
data.$el.css({
height: '',
width: '',
top: '',
bottom: '',
marginBottom: ''
}).removeClass(RawClasses.enabled);
data.$stickys.removeClass([RawClasses.passed, RawClasses.stuck].join(" "));
}
/**
* @method private
* @name renderRAF
* @description Updates DOM based on animation values
*/
function renderRAF() {
Functions.iterate.call($Instances, checkInstance);
}
/**
* @method
* @name resize
* @description Updates instance.
* @example $(".target").sticky("resize");
*/
/**
* @method private
* @name resizeInstance
* @description Handle window resize event
* @param data [object] "Instance data"
*/
function resizeInstance(data) {
if (data.enabled) {
cacheProps(data);
if (data.$container.length) {
var containerPos = data.$container.offset();
data.min = containerPos.top - data.margin;
data.max = data.min + data.$container.outerHeight(false) - data.height;
} else {
var $el;
if (data.stuck) {
$el = data.$clone;
} else {
$el = data.$el;
}
var elPos = $el.offset();
data.min = elPos.top - data.margin;
data.max = false;
}
checkInstance(data);
}
}
/**
* @method private
* @name cacheProps
* @description Cache instance props
* @param data [object] "Instance data"
*/
function cacheProps(data) {
var $el;
if (data.stuck) {
$el = data.$clone;
} else {
$el = data.$el;
}
data.margin = parseInt($el.css("margin-top"), 10);
if (data.$container.length) {
data.containerMargin = parseInt(data.$container.css("margin-top"), 10);
} else {
data.containerMargin = 0;
}
data.height = $el.outerHeight();
data.width = $el.outerWidth();
}
/**
* @method private
* @name checkInstance
* @description Handle window scroll event
* @param data [object] "Instance data"
*/
function checkInstance(data) {
if (data.enabled) {
var check = (ScrollTop + data.offset);
if (check >= data.min) {
data.stuck = true;
data.$stickys.addClass(RawClasses.stuck);
if (!data.stuck) {
data.$el.trigger(Events.stuck);
cacheProps(data);
}
var top = data.offset;
var bottom = '';
if (data.max && check > data.max) {
if (!data.passed) {
data.$el.trigger(Events.passed);
}
data.passed = true;
data.$stickys.addClass(RawClasses.passed);
top = '';
bottom = 0;
} else {
data.passed = false;
data.$stickys.removeClass(RawClasses.passed);
}
data.$el.css({
height: data.height,
width: data.width,
top: top,
bottom: bottom,
marginBottom: 0
});
} else {
data.stuck = false;
data.$stickys.removeClass(RawClasses.stuck).removeClass(RawClasses.passed);
if (data.stuck) {
data.$el.trigger(Events.unstuck);
// cacheProps(data);
}
data.$el.css({
height: '',
width: '',
top: '',
bottom: '',
marginBottom: ''
});
}
}
}
/**
* @plugin
* @name Sticky
* @description A jQuery plugin for sticky elements.
* @type widget
* @main sticky.js
* @dependency jQuery
* @dependency core.js
*/
var Plugin = Formstone.Plugin("sticky", {
widget: true,
/**
* @options
* @param maxWidth [string] <'Infinity'> "Width at which to auto-disable plugin"
* @param minWidth [string] <'0'> "Width at which to auto-disable plugin"
* @param offset [int] <0> "Element offset for activating sticky position"
*/
defaults: {
maxWidth: Infinity,
minWidth: '0px',
offset: 0
},
classes: [
"enabled",
"sticky",
"stuck",
"clone",
"container",
"passed"
],
/**
* @events
* @event passed.sticky "Element passed"
* @event stuck.sticky "Element stuck"
* @event unstuck.sticky "Element unstuck"
*/
events: {
passed: "passed",
stuck: "stuck",
unstuck: "unstuck"
},
methods: {
_construct: construct,
_postConstruct: postConstruct,
_destruct: destruct,
_resize: resize,
_raf: raf,
disable: disable,
enable: enable,
reset: resizeInstance,
resize: resizeInstance
}
}),
// Localize References
Namespace = Plugin.namespace,
Classes = Plugin.classes,
RawClasses = Classes.raw,
Events = Plugin.events,
Functions = Plugin.functions,
Window = Formstone.window,
$Window = Formstone.$window,
$Body,
ScrollTop = 0,
OldScrollTop = 0,
$Instances = [];
})
);