@@ -1,5 +1,5 @@
/*
* Clingify v1.0. 1
* Clingify v1.1
*
* A jQuery 1.7+ plugin for sticky elements
* http://github.com/theroux/clingify
@@ -14,153 +14,248 @@
// defaults
var pluginName = 'clingify' ,
// the name of using in .data()
dataPlugin = "plugin_" + pluginName ,
defaults = {
breakpoint : 0 ,
// Media query width breakpoint in pixels.
breakpointHeight : 0 ,
breakpointWidth : 0 ,
// Media query breakpoints in pixels.
// Below this value, Clingify behavior is disabled. (Useful for small screens.)
// Use 0 if you want Clingify to work at all screen widths.
// Use 0 if you want Clingify to work at all screen widths/heights .
extraClass : '' ,
// Add an additional class of your choosing to the sticky element
// and its parent wrapper & placeholder divs
throttle : 100 ,
throttle : 50 ,
// Delay Clingify functions, in milliseconds, when scrolling/resizing.
// Too fast is bad for performance, especially on older browsers/machines.
// Callback functions
detached : $ . noop , // Fires before element is detached
locked : $ . noop , // Fires before element is attached
resized : $ . noop // Fires after window resize event, benefits from the throttle
resized : $ . noop , // Fires after window resize event, benefits from the throttle
// Classes for CSS hooks
wrapperClass : 'js-clingify-wrapper' ,
lockedClass : 'js-clingify-locked' ,
overrideClass : 'js-clingify-permalock' ,
placeholderClass : 'js-clingify-placeholder'
} ,
wrapperClass = 'js-clingify-wrapper' ,
lockedClass = 'js-clingify-locked' ,
placeholderClass = 'js-clingify-placeholder' ,
$buildPlaceholder = $ ( '<div>' ) . addClass ( placeholderClass ) ,
$buildWrapper = $ ( '<div>' ) . addClass ( wrapperClass ) ,
$window = $ ( window ) ;
// plugin constructor
function Plugin ( element , options ) {
this . element = element ; // The element is the thing you passed to clingify()
/*
var privateMethod = function () {
console.log("private methods go here!");
};
*/
// turn our Clingify element into jQuery object
this . $element = $ ( element ) ;
// Overwrites defaults with options
this . options = $ . extend ( { } , defaults , options ) ;
this . _defaults = defaults ;
this . _name = pluginName ;
this . vars = {
elemHeight : this . $element . height ( )
} ;
this . init ( ) ;
}
// The actual plugin constructor
var Plugin = function ( element ) {
// Plugin instantiation
this . element = element ;
this . options = $ . extend ( { } , defaults ) ;
} ;
Plugin . prototype = {
init : function ( ) {
init : function ( options ) {
// extend options ( http://api.jquery.com/jQuery.extend/ )
$ . extend ( this . options , options ) ;
var cling = this ,
$elem = $ ( this . element ) ,
scrollTimeout ,
throttle = cling . options . throttle ,
extraClass = cling . options . extraClass ;
// Give Clingify element two wrapper divs.
// Placeholder div is set to same height as element
// This ensures content beneath element does not re-flow.
// Wrapper div is 100% width, which eases styling/centering/positioning
cling . $element
. wrap ( $buildPlaceholder . height ( cling . vars . elemHeight ) )
. wrap ( $buildWrapper ) ;
throttle = this . options . throttle ;
this . wrap ( ) ;
if ( ( extraClass !== '' ) && ( typeof extraClass === 'string' ) ) {
cling . findWrapper ( ) . addClass ( extraClass ) ;
cling . findPlaceholder ( ) . addClass ( extraClass ) ;
if ( ( this . options . extraClass !== "" ) && ( typeof this . options . extraClass === "string" ) ) {
this . findWrapper ( ) . addClass ( this . options . extraClass ) ;
this . findPlaceholder ( ) . addClass ( this . options . extraClass ) ;
this . options . wrapperClass += "." + this . options . extraClass ;
this . options . placeholderClass += "." + this . options . extraClass ;
}
this . bindScroll ( ) ;
this . bindResize ( ) ;
} ,
bindResize : function ( ) {
var cling = this ,
scrollTimeout ;
$window . on ( 'scroll resize' , function ( event ) {
$window . on ( 'resize.Clingify ' , function ( event ) {
if ( ! scrollTimeout ) {
scrollTimeout = setTimeout ( function ( ) {
if ( ( event . type === 'resize' ) && ( typeof cling . options . resized === 'function' ) ) {
cling . options . resized ( ) ;
}
cling . checkElemStatus ( ) ;
scrollTimeout = null ;
} , throttle ) ;
} , cling . options . throttle ) ;
}
} ) ;
} ,
bindScroll : function ( ) {
var cling = this ,
scrollTimeout ;
$window . on ( 'scroll.Clingify' , function ( event ) {
if ( ! scrollTimeout ) {
scrollTimeout = setTimeout ( function ( ) {
cling . checkElemStatus ( ) ;
scrollTimeout = null ;
} , cling . options . throttle ) ;
}
} ) ;
} ,
unbindResize : function ( ) {
$window . off ( 'resize.Clingify' ) ;
} ,
unbindScroll : function ( ) {
$window . off ( 'scroll.Clingify' ) ;
} ,
destroy : function ( ) {
this . unwrap ( ) ;
// unset Plugin data instance
this . element . removeData ( dataPlugin ) ;
return ;
} ,
//Other functions below
checkCoords : function ( ) {
var coords = {
windowWidth : $window . width ( ) ,
windowOffset : $window . scrollTop ( ) ,
// Y-position for Clingify placeholder
// needs to be recalculated in DOM has shifted
placeholderOffset : this . findPlaceholder ( ) . offset ( ) . top
} ;
windowHeight : $window . height ( ) ,
windowWidth : $window . width ( ) ,
windowOffset : $window . scrollTop ( ) ,
// Y-position for Clingify placeholder
// needs to be recalculated in DOM has shifted
placeholderOffset : this . findPlaceholder ( ) . offset ( ) . top
} ;
return coords ;
} ,
detachElem : function ( ) {
if ( typeof this . options . detached === 'function' ) {
this . options . detached ( ) ; // fire callback
}
this . findWrapper ( ) . removeClass ( lockedClass ) ;
if ( this . findWrapper ( ) . hasClass ( this . options . overrideClass ) ) {
return ;
} else {
this . findWrapper ( ) . removeClass ( this . options . lockedClass ) ;
}
return ;
} ,
lockElem : function ( ) {
if ( typeof this . options . locked === 'function' ) {
this . options . locked ( ) ; // fire callback
}
this . findWrapper ( ) . addClass ( lockedClass ) ;
this . findWrapper ( ) . addClass ( this . options . lockedClass ) ;
return ;
} ,
findPlaceholder : function ( ) {
return this . $element . closest ( '.' + placeholderClass ) ;
return this . $element . closest ( '.' + this . options . placeholderClass ) ;
} ,
findWrapper : function ( ) {
return this . $element . closest ( '.' + wrapperClass ) ;
return this . $element . closest ( '.' + this . options . wrapperClass ) ;
} ,
checkElemStatus : function ( ) {
var cling = this ,
currentCoords = cling . checkCoords ( ) ,
currentCoords = this . checkCoords ( ) ,
isScrolledPast = function ( ) {
if ( currentCoords . windowOffset >= currentCoords . placeholderOffset ) {
return true ;
} else {
return false ;
}
} ,
isWideEnough = function ( ) {
if ( currentCoords . windowWidth >= cling . options . breakpoint ) {
isWideTallEnough = function ( ) {
if ( ( currentCoords . windowWidth >= cling . options . breakpointWidth ) && currentCoords . windowHeight >= cling . options . breakpointHeight ) {
return true ;
} else {
return false ;
}
} ;
if ( isScrolledPast ( ) && isWideEnough ( ) ) {
cling . lockElem ( ) ;
} else if ( ! isScrolledPast ( ) || ! isWideEnough ( ) ) {
cling . detachElem ( ) ;
if ( isScrolledPast ( ) && isWideTallEnough ( ) ) {
this . lockElem ( ) ;
} else if ( ! isScrolledPast ( ) || ! isWideTallEnough ( ) ) {
this . detachElem ( ) ;
}
return ;
} ,
unwrap : function ( ) {
// Removes wrapper and placeholder
this . findPlaceholder ( ) . replaceWith ( this . element ) ;
return ;
} ,
test : function ( ) {
console . log ( 'Public test method is working!' ) ;
} ,
wrap : function ( ) {
// Creates wrapper and placeholder divs
var $buildPlaceholder = $ ( '<div>' ) . addClass ( this . options . placeholderClass ) ,
$buildWrapper = $ ( '<div>' ) . addClass ( this . options . wrapperClass ) ;
this . $element = $ ( this . element ) ;
this . elemHeight = this . $element . outerHeight ( ) ;
this . $element
. wrap ( $buildPlaceholder . height ( this . elemHeight ) )
. wrap ( $buildWrapper ) ;
this . findPlaceholder ( ) . height ( this . elemHeight ) ;
return ;
}
} ;
$ . fn [ pluginName ] = function ( arg ) {
// wrapper that prevents multiple instantiations
$ . fn [ pluginName ] = function ( options ) {
return this . each ( function ( ) {
if ( ! $ . data ( this , 'plugin_' + pluginName ) ) {
$ . data ( this , 'plugin_' + pluginName , new Plugin ( this , options ) ) ;
var args , instance ;
// only allow the plugin to be instantiated once
if ( ! ( this . data ( dataPlugin ) instanceof Plugin ) ) {
// if no instance, create one
this . data ( dataPlugin , new Plugin ( this ) ) ;
}
instance = this . data ( dataPlugin ) ;
instance . element = this ;
// Is the first parameter an object (arg), or was omitted,
// call Plugin.init( arg )
if ( typeof arg === 'undefined' || typeof arg === 'object' ) {
if ( typeof instance [ 'init' ] === 'function' ) {
instance . init ( arg ) ;
}
} ) ;
} ;
// checks that the requested public method exists
} else if ( typeof arg === 'string' && typeof instance [ arg ] === 'function' ) {
// copy arguments & remove function name
args = Array . prototype . slice . call ( arguments , 1 ) ;
// call the method
return instance [ arg ] . apply ( instance , args ) ;
} else {
$ . error ( 'Method ' + arg + ' does not exist on jQuery.' + pluginName ) ;
}
} ;
} ) ( jQuery , window , document ) ;