PageSourceSearch

https://www.niequistchiropractic.com/themes/common/javascripts/smb/jquery.fixed-scroll-block.js?v.1.258

js niequistchiropractic.com collected 2026-10-08 10:36:27 UTC 5,909 bytes, 195 lines download raw bytes

1/*
2 * FixedScrollBlock
3 */
4;(function($, window) {
5	'use strict';
6	var isMobileDevice = ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch) || /Windows Phone/.test(navigator.userAgent);
7
8	function FixedScrollBlock(options) {
9		this.options = $.extend({
10			fixedActiveClass: 'fixed-position',
11			slideBlock: '[data-scroll-block]',
12			positionType: 'auto',
13			fixedOnlyIfFits: true,
14			container: null,
15			animDelay: 100,
16			animSpeed: 200,
17			extraBottom: 0,
18			extraTop: 0
19		}, options);
20		this.initStructure();
21		this.attachEvents();
22	}
23	FixedScrollBlock.prototype = {
24		initStructure: function() {
25			// find elements
26			this.win = $(window);
27			this.container = $(this.options.container);
28			this.slideBlock = this.container.find(this.options.slideBlock);
29
30			// detect method
31			if(this.options.positionType === 'auto') {
32				this.options.positionType = isMobileDevice ? 'absolute' : 'fixed';
33			}
34		},
35		attachEvents: function() {
36			var self = this;
37
38			// bind events
39			this.onResize = function() {
40				self.resizeHandler();
41			};
42			this.onScroll = function() {
43				self.scrollHandler();
44			};
45
46			// handle events
47			this.win.on({
48				resize: this.onResize,
49				scroll: this.onScroll
50			});
51
52			// initial handler call
53			this.resizeHandler();
54		},
55		recalculateOffsets: function() {
56			var defaultOffset = this.slideBlock.offset(),
57				defaultPosition = this.slideBlock.position(),
58				holderOffset = this.container.offset(),
59				windowSize = this.win.height();
60
61			this.data = {
62				windowHeight: this.win.height(),
63				windowWidth: this.win.width(),
64
65				blockPositionLeft: defaultPosition.left,
66				blockPositionTop: defaultPosition.top,
67
68				blockOffsetLeft: defaultOffset.left,
69				blockOffsetTop: defaultOffset.top,
70				blockHeight: this.slideBlock.innerHeight(),
71
72				holderOffsetLeft: holderOffset.left,
73				holderOffsetTop: holderOffset.top,
74				holderHeight: this.container.innerHeight()
75			};
76		},
77		isVisible: function() {
78			return this.slideBlock.prop('offsetHeight');
79		},
80		fitsInViewport: function() {
81			if(this.options.fixedOnlyIfFits && this.data) {
82				return this.data.blockHeight + this.options.extraTop <= this.data.windowHeight;
83			} else {
84				return true;
85			}
86		},
87		resizeHandler: function() {
88			if (this.destroyed) return;
89			if(this.isVisible()) {
90				FixedScrollBlock.stickyMethods[this.options.positionType].onResize.apply(this, arguments);
91				this.scrollHandler();
92			}
93		},
94		scrollHandler: function() {
95			if (this.destroyed) return;
96			if(this.isVisible()) {
97				if(!this.data) {
98					this.resizeHandler();
99					return;
100				}
101				this.currentScrollTop = this.win.scrollTop();
102				this.currentScrollLeft = this.win.scrollLeft();
103				FixedScrollBlock.stickyMethods[this.options.positionType].onScroll.apply(this, arguments);
104			}
105		},
106		refresh: function() {
107			// refresh dimensions and state if needed
108			if(this.data) {
109				this.data.holderHeight = this.container.innerHeight();
110				this.data.blockHeight = this.slideBlock.innerHeight();
111				this.scrollHandler();
112			}
113		},
114		destroy: function() {
115			this.destroyed = true;
116			// remove event handlers and styles
117			this.slideBlock.removeAttr('style').removeClass(this.options.fixedActiveClass);
118			this.win.off({
119				resize: this.onResize,
120				scroll: this.onScroll
121			});
122		}
123	};
124
125	// sticky methods
126	FixedScrollBlock.stickyMethods = {
127		fixed: {
128			onResize: function() {
129				this.slideBlock.removeAttr('style');
130				this.recalculateOffsets();
131			},
132			onScroll: function() {
133				if(this.fitsInViewport() && this.currentScrollTop + this.options.extraTop > this.data.blockOffsetTop) {
134					if(this.currentScrollTop + this.options.extraTop + this.data.blockHeight > this.data.holderOffsetTop + this.data.holderHeight - this.options.extraBottom) {
135						this.slideBlock.css({
136							position: 'absolute',
137							top: this.data.blockPositionTop + this.data.holderHeight - this.data.blockHeight - this.options.extraBottom - (this.data.blockOffsetTop - this.data.holderOffsetTop),
138							left: this.data.blockPositionLeft
139						});
140					} else {
141						this.slideBlock.css({
142							position: 'fixed',
143							top: this.options.extraTop,
144							left: this.data.blockOffsetLeft - this.currentScrollLeft
145						});
146					}
147					this.slideBlock.addClass(this.options.fixedActiveClass);
148				} else {
149					this.slideBlock.removeClass(this.options.fixedActiveClass).removeAttr('style');
150				}
151			}
152		},
153		absolute: {
154			onResize: function() {
155				this.slideBlock.removeAttr('style');
156				this.recalculateOffsets();
157
158				this.slideBlock.css({
159					position: 'absolute',
160					top: this.data.blockPositionTop,
161					left: this.data.blockPositionLeft
162				});
163
164				this.slideBlock.addClass(this.options.fixedActiveClass);
165			},
166			onScroll: function() {
167				var self = this;
168				clearTimeout(this.animTimer);
169				this.animTimer = setTimeout(function() {
170					var currentScrollTop = self.currentScrollTop + self.options.extraTop,
171						initialPosition = self.data.blockPositionTop - (self.data.blockOffsetTop - self.data.holderOffsetTop),
172						maxTopPosition =  self.data.holderHeight - self.data.blockHeight - self.options.extraBottom,
173						currentTopPosition = initialPosition + Math.min(currentScrollTop - self.data.holderOffsetTop, maxTopPosition),
174						calcTopPosition = self.fitsInViewport() && currentScrollTop > self.data.blockOffsetTop ? currentTopPosition : self.data.blockPositionTop;
175
176					self.slideBlock.stop().animate({
177						top: calcTopPosition
178					}, self.options.animSpeed);
179				}, this.options.animDelay);
180			}
181		}
182	};
183
184	// jQuery plugin interface
185	$.fn.fixedScrollBlock = function(options) {
186		return this.each(function() {
187			var params = $.extend({}, options, {container: this}),
188				instance = new FixedScrollBlock(params);
189			$.data(this, 'FixedScrollBlock', instance);
190		});
191	};
192
193	// module exports
194	window.FixedScrollBlock = FixedScrollBlock;
195}(jQuery, this));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.