2 * jQuery ScrollSpy Plugin
3 * Author: @sxalexander, softwarespot
4 * Licensed under the MIT license
6 (function jQueryScrollspy(window, $) {
10 scrollspy: function scrollspy(options, action) {
11 // If the options parameter is a string, then assume it's an 'action', therefore swap the parameters around
12 if (_isString(options)) {
13 var tempOptions = action;
15 // Set the action as the option parameter
18 // Set to be the reference action pointed to
19 options = tempOptions;
22 // override the default options with those passed to the plugin
23 options = $.extend({}, _defaults, options);
25 // sanitize the following option with the default value if the predicate fails
26 _sanitizeOption(options, _defaults, 'container', _isObject);
28 // cache the jQuery object
29 var $container = $(options.container);
31 // check if it's a valid jQuery selector
32 if ($container.length === 0) {
36 // sanitize the following option with the default value if the predicate fails
37 _sanitizeOption(options, _defaults, 'namespace', _isString);
39 // check if the action is set to DESTROY/destroy
40 if (_isString(action) && action.toUpperCase() === 'DESTROY') {
41 $container.off('scroll.' + options.namespace);
45 // sanitize the following options with the default values if the predicates fails
46 _sanitizeOption(options, _defaults, 'buffer', $.isNumeric);
47 _sanitizeOption(options, _defaults, 'max', $.isNumeric);
48 _sanitizeOption(options, _defaults, 'min', $.isNumeric);
51 _sanitizeOption(options, _defaults, 'onEnter', $.isFunction);
52 _sanitizeOption(options, _defaults, 'onLeave', $.isFunction);
53 _sanitizeOption(options, _defaults, 'onLeaveTop', $.isFunction);
54 _sanitizeOption(options, _defaults, 'onLeaveBottom', $.isFunction);
55 _sanitizeOption(options, _defaults, 'onTick', $.isFunction);
57 if ($.isFunction(options.max)) {
58 options.max = options.max();
61 if ($.isFunction(options.min)) {
62 options.min = options.min();
65 // check if the mode is set to VERTICAL/vertical
66 var isVertical = window.String(options.mode).toUpperCase() === 'VERTICAL';
68 return this.each(function each() {
72 // cache the jQuery object
73 var $element = $(_this);
75 // count the number of times a container is entered
78 // determine if the scroll is with inside the container
81 // count the number of times a container is left
84 // create a scroll listener for the container
85 $container.on('scroll.' + options.namespace, function onScroll() {
86 // cache the jQuery object
89 // create a position object literal
91 top: $this.scrollTop(),
92 left: $this.scrollLeft(),
95 var containerHeight = $container.height();
97 var max = options.max;
99 var min = options.min;
101 var xAndY = isVertical ? position.top + options.buffer : position.left + options.buffer;
104 // get the maximum value based on either the height or the outer width
105 max = isVertical ? containerHeight : $container.outerWidth() + $element.outerWidth();
108 // if we have reached the minimum bound, though are below the max
109 if (xAndY >= min && xAndY <= max) {
110 // trigger the 'scrollEnter' event
115 // trigger the 'scrollEnter' event
116 $element.trigger('scrollEnter', {
120 // call the 'onEnter' function
121 if (options.onEnter !== null) {
122 options.onEnter(_this, position);
126 // trigger the 'scrollTick' event
127 $element.trigger('scrollTick', {
134 // call the 'onTick' function
135 if (options.onTick !== null) {
136 options.onTick(_this, position, inside, enters, leaves);
143 // trigger the 'scrollLeave' event
144 $element.trigger('scrollLeave', {
149 // call the 'onLeave' function
150 if (options.onLeave !== null) {
151 options.onLeave(_this, position);
155 // trigger the 'scrollLeaveTop' event
156 $element.trigger('scrollLeaveTop', {
161 // call the 'onLeaveTop' function
162 if (options.onLeaveTop !== null) {
163 options.onLeaveTop(_this, position);
165 } else if (xAndY >= max) {
166 // trigger the 'scrollLeaveBottom' event
167 $element.trigger('scrollLeaveBottom', {
172 // call the 'onLeaveBottom' function
173 if (options.onLeaveBottom !== null) {
174 options.onLeaveBottom(_this, position);
178 // Idea taken from: http://stackoverflow.com/questions/5353934/check-if-element-is-visible-on-screen
179 var containerScrollTop = $container.scrollTop();
181 // Get the element height
182 var elementHeight = $element.height();
184 // Get the element offset
185 var elementOffsetTop = $element.offset().top;
187 if ((elementOffsetTop < (containerHeight + containerScrollTop)) && (elementOffsetTop > (containerScrollTop - elementHeight))) {
188 // trigger the 'scrollView' event
189 $element.trigger('scrollView', {
193 // call the 'onView' function
194 if (options.onView !== null) {
195 options.onView(_this, position);
211 // the offset to be applied to the left and top positions of the container
214 // the element to apply the 'scrolling' event to (default window)
217 // the maximum value of the X or Y coordinate, depending on mode the selected
220 // the maximum value of the X or Y coordinate, depending on mode the selected
223 // whether to listen to the X (horizontal) or Y (vertical) scrolling
226 // namespace to append to the 'scroll' event
227 namespace: 'scrollspy',
229 // call the following callback function every time the user enters the min / max zone
232 // call the following callback function every time the user leaves the min / max zone
235 // call the following callback function every time the user leaves the top zone
238 // call the following callback function every time the user leaves the bottom zone
241 // call the following callback function on each scroll event within the min and max parameters
244 // call the following callback function on each scroll event when the element is inside the viewable view port
250 // check if a value is an object datatype
251 function _isObject(value) {
252 return $.type(value) === 'object';
255 // check if a value is a string datatype with a length greater than zero when whitespace is stripped
256 function _isString(value) {
257 return $.type(value) === 'string' && $.trim(value).length > 0;
260 // check if an option is correctly formatted using a predicate; otherwise, return the default value
261 function _sanitizeOption(options, defaults, property, predicate) {
262 // set the property to the default value if the predicate returned false
263 if (!predicate(options[property])) {
264 options[property] = defaults[property];
267 }(window, window.jQuery));