| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- // jQuery HC-Sticky
- // =============
- // Version: 1.2.43
- // Copyright: Some Web Media
- // Author: Some Web Guy
- // Author URL: http://twitter.com/some_web_guy
- // Website: http://someweblog.com/
- // Plugin URL: https://github.com/somewebmedia/hc-sticky
- // License: Released under the MIT License www.opensource.org/licenses/mit-license.php
- // Description: Cross-browser jQuery plugin that makes any element attached to the page and always visible while you scroll.
- (function($, window, undefined) {
- "use strict";
- // console.log shortcut
- var log = function(t){console.log(t)};
- var $window = $(window),
- document = window.document,
- $document = $(document);
- // detect IE version
- var ie = (function(){var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i'); while (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0]){}; return v > 4 ? v : undef})();
- /*----------------------------------------------------
- Global functions
- ----------------------------------------------------*/
- // check for scroll direction and speed
- var getScroll = function() {
- var pageXOffset = window.pageXOffset !== undefined ? window.pageXOffset : (document.compatMode == "CSS1Compat" ? window.document.documentElement.scrollLeft : window.document.body.scrollLeft),
- pageYOffset = window.pageYOffset !== undefined ? window.pageYOffset : (document.compatMode == "CSS1Compat" ? window.document.documentElement.scrollTop : window.document.body.scrollTop);
- if (typeof getScroll.x == 'undefined') {
- getScroll.x = pageXOffset;
- getScroll.y = pageYOffset;
- }
- if (typeof getScroll.distanceX == 'undefined') {
- getScroll.distanceX = pageXOffset;
- getScroll.distanceY = pageYOffset;
- } else {
- getScroll.distanceX = pageXOffset - getScroll.x;
- getScroll.distanceY = pageYOffset - getScroll.y;
- }
- var diffX = getScroll.x - pageXOffset,
- diffY = getScroll.y - pageYOffset;
- getScroll.direction = diffX < 0 ? 'right' :
- diffX > 0 ? 'left' :
- diffY <= 0 ? 'down' :
- diffY > 0 ? 'up' : 'first';
- getScroll.x = pageXOffset;
- getScroll.y = pageYOffset;
- };
- $window.on('scroll', getScroll);
- // little original style plugin
- $.fn.style = function(style) {
- if (!style) return null;
- var $this = $(this),
- value;
- // clone element
- var $clone = $this.clone().css('display','none');
- // randomize the name of cloned radio buttons, otherwise selections get screwed
- $clone.find('input:radio').attr('name','copy-' + Math.floor((Math.random()*100)+1));
- // insert clone to DOM
- $this.after($clone);
- var getStyle = function(el, style){
- var val;
- if (el.currentStyle) {
- // replace dashes with capitalized letter, e.g. padding-left to paddingLeft
- val = el.currentStyle[style.replace(/-\w/g, function(s){return s.toUpperCase().replace('-','')})];
- } else if (window.getComputedStyle) {
- val = document.defaultView.getComputedStyle(el,null).getPropertyValue(style);
- }
- // check for margin:auto
- val = (/margin/g.test(style)) ? ((parseInt(val) === $this[0].offsetLeft) ? val : 'auto') : val;
- return val;
- };
- if (typeof style == 'string') {
- value = getStyle($clone[0], style);
- } else {
- value = {};
- $.each(style, function(i, s){
- value[s] = getStyle($clone[0], s);
- });
- }
- // destroy clone
- $clone.remove();
- return value || null;
- };
- /*----------------------------------------------------
- jQuery plugin
- ----------------------------------------------------*/
- $.fn.extend({
- hcSticky: function(options) {
- // check if selected element exist in DOM, user doesn't have to worry about that
- if (this.length == 0) return this;
- this.pluginOptions('hcSticky', {
- top: 0,
- bottom: 0,
- bottomEnd: 0,
- innerTop: 0,
- innerSticker: null,
- className: 'sticky',
- wrapperClassName: 'wrapper-sticky',
- stickTo: null,
- responsive: true,
- followScroll: true,
- offResolutions: null,
- onStart: $.noop,
- onStop: $.noop,
- on: true,
- fn: null // used only by the plugin
- }, options || {}, {
- reinit: function(){
- // just call itself again
- $(this).hcSticky();
- },
- stop: function(){
- $(this).pluginOptions('hcSticky', {on: false}).each(function(){
- var $this = $(this),
- options = $this.pluginOptions('hcSticky'),
- $wrapper = $this.parent('.' + options.wrapperClassName);
- // set current position
- var top = $this.offset().top - $wrapper.offset().top;
- $this.css({
- position: 'absolute',
- top: top,
- bottom: 'auto',
- left: 'auto',
- right: 'auto'
- }).removeClass(options.className);
- });
- },
- off: function(){
- $(this).pluginOptions('hcSticky', {on: false}).each(function(){
- var $this = $(this),
- options = $this.pluginOptions('hcSticky'),
- $wrapper = $this.parent('.' + options.wrapperClassName);
- // clear position
- $this.css({
- position: 'relative',
- top: 'auto',
- bottom: 'auto',
- left: 'auto',
- right: 'auto'
- }).removeClass(options.className);
- $wrapper.css('height', 'auto');
- });
- },
- on: function(){
- $(this).each(function(){
- $(this).pluginOptions('hcSticky', {
- on: true,
- remember: {
- offsetTop: $window.scrollTop()
- }
- }).hcSticky();
- });
- },
- destroy: function(){
- var $this = $(this),
- options = $this.pluginOptions('hcSticky'),
- $wrapper = $this.parent('.' + options.wrapperClassName);
- // reset position to original
- $this.removeData('hcStickyInit').css({
- position: $wrapper.css('position'),
- top: $wrapper.css('top'),
- bottom: $wrapper.css('bottom'),
- left: $wrapper.css('left'),
- right: $wrapper.css('right')
- }).removeClass(options.className);
- // remove events
- $window.off('resize', options.fn.resize).off('scroll', options.fn.scroll);
- // destroy wrapper
- $this.unwrap();
- }
- });
- // on/off settings
- if (options && typeof options.on != 'undefined') {
- if (options.on) {
- this.hcSticky('on');
- } else {
- this.hcSticky('off');
- }
- }
- // stop on commands
- if (typeof options == 'string') return this;
- // do our thing
- return this.each(function(){
- var $this = $(this),
- options = $this.pluginOptions('hcSticky');
- var $wrapper = (function(){ // wrapper exists
- var $this_wrapper = $this.parent('.' + options.wrapperClassName);
- if ($this_wrapper.length > 0) {
- $this_wrapper.css({
- 'height': $this.outerHeight(true),
- 'width': (function(){
- // check if wrapper already has width in %
- var width = $this_wrapper.style('width');
- if (width.indexOf('%') >= 0 || width == 'auto') {
- if ($this.css('box-sizing') == 'border-box' || $this.css('-moz-box-sizing') == 'border-box') {
- $this.css('width', $this_wrapper.width());
- } else {
- $this.css('width', $this_wrapper.width() - parseInt($this.css('padding-left') - parseInt($this.css('padding-right'))));
- }
- return width;
- } else {
- return $this.outerWidth(true);
- }
- })()
- });
- return $this_wrapper;
- } else {
- return false;
- }
- })() || (function(){ // wrapper doesn't exist
- var this_css = $this.style(['width', 'margin-left', 'left', 'right', 'top', 'bottom', 'float', 'display']);
- var display = $this.css('display');
- var $this_wrapper = $('<div>', {
- 'class': options.wrapperClassName
- }).css({
- 'display': display,
- 'height': $this.outerHeight(true),
- 'width': (function(){
- if (this_css['width'].indexOf('%') >= 0 || (this_css['width'] == 'auto' && display != 'inline-block' && display != 'inline')) { // check if element has width in %
- $this.css('width', parseFloat($this.css('width')));
- return this_css['width'];
- } else if (this_css['width'] == 'auto' && (display == 'inline-block' || display == 'inline')) {
- return $this.width();
- } else {
- // check if margin is set to 'auto'
- return (this_css['margin-left'] == 'auto') ? $this.outerWidth() : $this.outerWidth(true);
- }
- })(),
- 'margin': (this_css['margin-left']) ? 'auto' : null,
- 'position': (function(){
- var position = $this.css('position');
- return position == 'static' ? 'relative' : position;
- })(),
- 'float': this_css['float'] || null,
- 'left': this_css['left'],
- 'right': this_css['right'],
- 'top': this_css['top'],
- 'bottom': this_css['bottom'],
- 'vertical-align': 'top'
- });
- $this.wrap($this_wrapper);
- // ie7 inline-block fix
- if (ie === 7) {
- if ($('head').find('style#hcsticky-iefix').length === 0) {
- $('<style id="hcsticky-iefix">.' + options.wrapperClassName + ' {zoom: 1;}</style>').appendTo('head');
- }
- }
- // return appended element
- return $this.parent();
- })();
- // check if we should go further
- if ($this.data('hcStickyInit')) return;
- // leave our mark
- $this.data('hcStickyInit', true);
- // check if referring element is document
- var stickTo_document = options.stickTo && (options.stickTo == 'document' || (options.stickTo.nodeType && options.stickTo.nodeType == 9) || (typeof options.stickTo == 'object' && options.stickTo instanceof (typeof HTMLDocument != 'undefined' ? HTMLDocument : Document))) ? true : false;
- // select container ;)
- var $container = options.stickTo
- ? stickTo_document
- ? $document
- : typeof options.stickTo == 'string'
- ? $(options.stickTo)
- : options.stickTo
- : $wrapper.parent();
- // clear sticky styles
- $this.css({
- top: 'auto',
- bottom: 'auto',
- left: 'auto',
- right: 'auto'
- });
- // attach event on entire page load, maybe some images inside element has been loading, so chek height again
- $window.load(function(){
- if ($this.outerHeight(true) > $container.height()) {
- $wrapper.css('height', $this.outerHeight(true));
- $this.hcSticky('reinit');
- }
- });
- // functions for attachiung and detaching sticky
- var _setFixed = function(args) {
- // check if already floating
- if ($this.hasClass(options.className)) return;
- // apply styles
- args = args || {};
- $this.css({
- position: 'fixed',
- top: args.top || 0,
- left: args.left || $wrapper.offset().left
- }).addClass(options.className);
- // start event
- options.onStart.apply($this[0]);
- // add class to wrpaeer
- $wrapper.addClass('sticky-active');
- },
- _reset = function(args) {
- args = args || {};
- args.position = args.position || 'absolute';
- args.top = args.top || 0;
- args.left = args.left || 0;
- // check if we should apply css
- if ($this.css('position') != 'fixed' && parseInt($this.css('top')) == args.top) return;
- // apply styles
- $this.css({
- position: args.position,
- top: args.top,
- left: args.left
- }).removeClass(options.className);
- // stop event
- options.onStop.apply($this[0]);
- // remove class from wrpaeer
- $wrapper.removeClass('sticky-active');
- };
- // sticky scroll function
- var onScroll = function(init) {
- // check if we need to run sticky
- if (!options.on || $this.outerHeight(true) >= $container.height()) return;
- var top_spacing = (options.innerSticker) ? $(options.innerSticker).position().top : ((options.innerTop) ? options.innerTop : 0),
- wrapper_inner_top = $wrapper.offset().top,
- bottom_limit = $container.height() - options.bottomEnd + (stickTo_document ? 0 : wrapper_inner_top),
- top_limit = $wrapper.offset().top - options.top + top_spacing,
- this_height = $this.outerHeight(true) + options.bottom,
- window_height = $window.height(),
- offset_top = $window.scrollTop(),
- this_document_top = $this.offset().top,
- this_window_top = this_document_top - offset_top,
- bottom_distance;
- // if sticky has been restarted with on/off wait for it to reach top or bottom
- if (typeof options.remember != 'undefined' && options.remember) {
- var position_top = this_document_top - options.top - top_spacing;
- if (this_height - top_spacing > window_height && options.followScroll) { // element bigger than window with follow scroll on
- if (position_top < offset_top && offset_top + window_height <= position_top + $this.height()) {
- // element is in the middle of the screen, let our primary calculations do the work
- options.remember = false;
- }
- } else { // element smaller than window or follow scroll turned off
- if (options.remember.offsetTop > position_top) {
- // slide up
- if (offset_top <= position_top) {
- _setFixed({
- top: options.top - top_spacing
- });
- options.remember = false;
- }
- } else {
- // slide down
- if (offset_top >= position_top) {
- _setFixed({
- top: options.top - top_spacing
- });
- options.remember = false;
- }
- }
- }
- return;
- }
- if (offset_top > top_limit) {
- // http://geek-and-poke.com/geekandpoke/2012/7/27/simply-explained.html
- if (bottom_limit + options.bottom - (options.followScroll && window_height < this_height ? 0 : options.top) <= offset_top + this_height - top_spacing - ((this_height - top_spacing > window_height - (top_limit - top_spacing) && options.followScroll) ? (((bottom_distance = this_height - window_height - top_spacing) > 0) ? bottom_distance : 0) : 0)) {
- // bottom reached end
- _reset({
- top: bottom_limit - this_height + options.bottom - wrapper_inner_top
- });
- } else if (this_height - top_spacing > window_height && options.followScroll) {
- if (this_window_top + this_height <= window_height) { // element bigger than window with follow scroll on
- if (getScroll.direction == 'down') {
- // scroll down
- _setFixed({
- top: window_height - this_height
- });
- } else {
- // scroll up
- if (this_window_top < 0 && $this.css('position') == 'fixed') {
- _reset({
- top: this_document_top - (top_limit + options.top - top_spacing) - getScroll.distanceY
- });
- }
- }
- } else { // element smaller than window or follow scroll turned off
- if (getScroll.direction == 'up' && this_document_top >= offset_top + options.top - top_spacing) {
- // scroll up
- _setFixed({
- top: options.top - top_spacing
- });
- } else if (getScroll.direction == 'down' && this_document_top + this_height > window_height && $this.css('position') == 'fixed') {
- // scroll down
- _reset({
- top: this_document_top - (top_limit + options.top - top_spacing) - getScroll.distanceY
- });
- }
- }
- } else {
- // starting (top) fixed position
- _setFixed({
- top: options.top - top_spacing
- });
- }
- } else {
- // reset
- _reset();
- }
- };
- // store resize data in case responsive is on
- var resize_timeout = false,
- $resize_clone = false;
- var onResize = function() {
- // check if sticky is attached to scroll event
- attachScroll();
- // check for off resolutions
- checkResolutions();
- // check if we need to run sticky
- if (!options.on) return;
- var setLeft = function(){
- // set new left position
- if ($this.css('position') == 'fixed') {
- $this.css('left', $wrapper.offset().left);
- } else {
- $this.css('left', 0);
- }
- };
- // check for width change (css media queries)
- if (options.responsive) {
- // clone element and make it invisible
- if (!$resize_clone) {
- $resize_clone = $this.clone().attr('style', '').css({
- visibility: 'hidden',
- height: 0,
- overflow: 'hidden',
- paddingTop: 0,
- paddingBottom: 0,
- marginTop: 0,
- marginBottom: 0
- });
- $wrapper.after($resize_clone);
- }
- var wrapper_width = $wrapper.style('width');
- var resize_clone_width = $resize_clone.style('width');
- if (resize_clone_width == 'auto' && wrapper_width != 'auto') {
- resize_clone_width = parseInt($this.css('width'));
- }
- // recalculate wrpaeer width
- if (resize_clone_width != wrapper_width) {
- $wrapper.width(resize_clone_width);
- }
- // clear previous timeout
- if (resize_timeout) {
- clearTimeout(resize_timeout);
- }
- // timedout destroing of cloned elements so we don't clone it again and again while resizing the window
- resize_timeout = setTimeout(function() {
- // clear timeout id
- resize_timeout = false;
- // destroy cloned element
- $resize_clone.remove();
- $resize_clone = false;
- }, 250);
- }
- // set new left position
- setLeft();
- // recalculate inner element width (maybe original width was in %)
- if ($this.outerWidth(true) != $wrapper.width()) {
- var this_w = ($this.css('box-sizing') == 'border-box' || $this.css('-moz-box-sizing') == 'border-box')
- ? $wrapper.width()
- : $wrapper.width() - parseInt($this.css('padding-left')) - parseInt($this.css('padding-right'));
- // subtract margins
- this_w = this_w - parseInt($this.css('margin-left')) - parseInt($this.css('margin-right'));
- // set new width
- $this.css('width', this_w);
- }
- };
- // remember scroll and resize callbacks so we can attach and detach them
- $this.pluginOptions('hcSticky', {fn: {
- scroll: onScroll,
- resize: onResize
- }});
- // check for off resolutions
- var checkResolutions = function(){
- if (options.offResolutions) {
- // convert to array
- if (!$.isArray(options.offResolutions)) {
- options.offResolutions = [options.offResolutions];
- }
- var isOn = true;
- $.each(options.offResolutions, function(i, rez){
- if (rez < 0) {
- // below
- if ($window.width() < rez * -1) {
- isOn = false;
- $this.hcSticky('off');
- }
- } else {
- // abowe
- if ($window.width() > rez) {
- isOn = false;
- $this.hcSticky('off');
- }
- }
- });
- // turn on again
- if (isOn && !options.on) {
- $this.hcSticky('on');
- }
- }
- };
- checkResolutions();
- // attach resize function to event
- $window.on('resize', onResize);
- // attaching scroll function to event
- var attachScroll = function(){
- // check if element height is bigger than the content
- if ($this.outerHeight(true) < $container.height()) {
- var isAttached = false;
- if ($._data(window, 'events').scroll != undefined) {
- $.each($._data(window, 'events').scroll, function(i, f){
- if (f.handler == options.fn.scroll) {
- isAttached = true;
- }
- });
- }
- if (!isAttached) {
- // run it once to disable glitching
- options.fn.scroll(true);
- // attach function to scroll event only once
- $window.on('scroll', options.fn.scroll);
- }
- }
- };
- attachScroll();
- });
- }
- });
- })(jQuery, this);
- // jQuery HC-PluginOptions
- // =============
- // Version: 1.0
- // Copyright: Some Web Media
- // Author: Some Web Guy
- // Author URL: http://twitter.com/some_web_guy
- // Website: http://someweblog.com/
- // License: Released under the MIT License www.opensource.org/licenses/mit-license.php
- (function($, undefined) {
- "use strict";
- $.fn.extend({
- pluginOptions: function(pluginName, defaultOptions, userOptions, commands) {
- // create object to store data
- if (!this.data(pluginName)) this.data(pluginName, {});
- // return options
- if (pluginName && typeof defaultOptions == 'undefined') return this.data(pluginName).options;
- // update
- userOptions = userOptions || (defaultOptions || {});
- if (typeof userOptions == 'object' || userOptions === undefined) {
- // options
- return this.each(function(){
- var $this = $(this);
- if (!$this.data(pluginName).options) {
- // init our options and attach to element
- $this.data(pluginName, {options: $.extend(defaultOptions, userOptions || {})});
- // attach commands if any
- if (commands) {
- $this.data(pluginName).commands = commands;
- }
- } else {
- // update existing options
- $this.data(pluginName, $.extend($this.data(pluginName), {options: $.extend($this.data(pluginName).options, userOptions || {})}));
- }
- });
- } else if (typeof userOptions == 'string') {
- return this.each(function(){
- $(this).data(pluginName).commands[userOptions].call(this);
- });
- } else {
- return this;
- }
- }
- });
- })(jQuery);
|