Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1738a14
commit b764b97
Showing
17 changed files
with
8,550 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.fixedsticky { | ||
position: -webkit-sticky; | ||
position: -moz-sticky; | ||
position: -ms-sticky; | ||
position: -o-sticky; | ||
position: sticky; | ||
} | ||
/* When position: sticky is supported but native behavior is ignored */ | ||
.fixedsticky-withoutfixedfixed .fixedsticky-off, | ||
.fixed-supported .fixedsticky-off { | ||
position: static; | ||
} | ||
.fixedsticky-withoutfixedfixed .fixedsticky-on, | ||
.fixed-supported .fixedsticky-on { | ||
position: fixed; | ||
} | ||
.fixedsticky-dummy { | ||
display: none; | ||
} | ||
.fixedsticky-on + .fixedsticky-dummy { | ||
display: block; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
;(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( $( '<div>' ).addClass( 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 ); |
Oops, something went wrong.