;(function( win, $ ) { function featureTest( property, value, noPrefixes ) { // Thanks Modernizr! https://github.com/phistuck/Modernizr/commit/3fb7217f5f8274e2f11fe6cfeda7cfaf9948a1f5 var prop = property + ':', el = document.createElement( 'test' ), mStyle = el.style; if( !noPrefixes ) { mStyle.cssText = prop + [ '-webkit-', '-moz-', '-ms-', '-o-', '' ].join( value + ';' + prop ) + value + ';'; } else { mStyle.cssText = prop + value; } return mStyle[ property ].indexOf( value ) !== -1; } function getPx( unit ) { return parseInt( unit, 10 ) || 0; } var uniqueIdCounter = 0; var S = { classes: { plugin: 'fixedsticky', active: 'fixedsticky-on', inactive: 'fixedsticky-off', clone: 'fixedsticky-dummy', withoutFixedFixed: 'fixedsticky-withoutfixedfixed' }, keys: { offset: 'fixedStickyOffset', position: 'fixedStickyPosition', id: 'fixedStickyId' }, tests: { sticky: featureTest( 'position', 'sticky' ), fixed: featureTest( 'position', 'fixed', true ) }, // Thanks jQuery! getScrollTop: function() { var prop = 'pageYOffset', method = 'scrollTop'; return win ? (prop in win) ? win[ prop ] : win.document.documentElement[ method ] : win.document.body[ method ]; }, bypass: function() { // Check native sticky, check fixed and if fixed-fixed is also included on the page and is supported return ( S.tests.sticky && !S.optOut ) || !S.tests.fixed || win.FixedFixed && !$( win.document.documentElement ).hasClass( 'fixed-supported' ); }, update: function( el ) { if( !el.offsetWidth ) { return; } var $el = $( el ), height = $el.outerHeight(), initialOffset = $el.data( S.keys.offset ), scroll = S.getScrollTop(), isAlreadyOn = $el.is( '.' + S.classes.active ), toggle = function( turnOn ) { $el[ turnOn ? 'addClass' : 'removeClass' ]( S.classes.active ) [ !turnOn ? 'addClass' : 'removeClass' ]( S.classes.inactive ); }, viewportHeight = $( window ).height(), position = $el.data( S.keys.position ), skipSettingToFixed, elTop, elBottom, $parent = $el.parent(), parentOffset = $parent.offset().top, parentHeight = $parent.outerHeight(); if( initialOffset === undefined ) { initialOffset = $el.offset().top; $el.data( S.keys.offset, initialOffset ); $el.after( $( '
' ).addClass( S.classes.clone ).height( height ) ); } else { $el.next( '.' + S.classes.clone ).height( height ); } if( !position ) { // Some browsers require fixed/absolute to report accurate top/left values. skipSettingToFixed = $el.css( 'top' ) !== 'auto' || $el.css( 'bottom' ) !== 'auto'; if( !skipSettingToFixed ) { $el.css( 'position', 'fixed' ); } position = { top: $el.css( 'top' ) !== 'auto', bottom: $el.css( 'bottom' ) !== 'auto' }; if( !skipSettingToFixed ) { $el.css( 'position', '' ); } $el.data( S.keys.position, position ); } function isFixedToTop() { var offsetTop = scroll + elTop; // Initial Offset Top return initialOffset < offsetTop && // Container Bottom offsetTop + height <= parentOffset + parentHeight; } function isFixedToBottom() { // Initial Offset Top + Height return initialOffset + ( height || 0 ) > scroll + viewportHeight - elBottom && // Container Top scroll + viewportHeight - elBottom >= parentOffset + ( height || 0 ); } elTop = getPx( $el.css( 'top' ) ); elBottom = getPx( $el.css( 'bottom' ) ); if( position.top && isFixedToTop() || position.bottom && isFixedToBottom() ) { if( !isAlreadyOn ) { toggle( true ); } } else { if( isAlreadyOn ) { toggle( false ); } } }, destroy: function( el ) { var $el = $( el ); if (S.bypass()) { return $el; } return $el.each(function() { var $this = $( this ); var id = $this.data( S.keys.id ); $( win ).unbind( '.fixedsticky' + id ); $this .removeData( [ S.keys.offset, S.keys.position, S.keys.id ] ) .removeClass( S.classes.active ) .removeClass( S.classes.inactive ) .next( '.' + S.classes.clone ).remove(); }); }, init: function( el ) { var $el = $( el ); if( S.bypass() ) { return $el; } return $el.each(function() { var _this = this; var id = uniqueIdCounter++; $( this ).data( S.keys.id, id ); $( win ).bind( 'scroll.fixedsticky' + id, function() { S.update( _this ); }).trigger( 'scroll.fixedsticky' + id ); $( win ).bind( 'resize.fixedsticky' + id , function() { if( $el.is( '.' + S.classes.active ) ) { S.update( _this ); } }); }); } }; win.FixedSticky = S; // Plugin $.fn.fixedsticky = function( method ) { if ( typeof S[ method ] === 'function') { return S[ method ].call( S, this); } else if ( typeof method === 'object' || ! method ) { return S.init.call( S, this ); } else { throw new Error( 'Method `' + method + '` does not exist on jQuery.fixedsticky' ); } }; // Add fallback when fixed-fixed is not available. if( !win.FixedFixed ) { $( win.document.documentElement ).addClass( S.classes.withoutFixedFixed ); } })( window, jQuery );