first commit
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Extend jquery with a scrollspy plugin.
|
||||
* This watches the window scroll and fires events when elements are scrolled into viewport.
|
||||
*
|
||||
* throttle() and getTime() taken from Underscore.js
|
||||
* https://github.com/jashkenas/underscore
|
||||
*
|
||||
* @author Copyright 2013 John Smart
|
||||
* @license https://raw.github.com/thesmart/jquery-scrollspy/master/LICENSE
|
||||
* @see https://github.com/thesmart
|
||||
* @version 0.1.2
|
||||
*/
|
||||
(function($) {
|
||||
|
||||
var jWindow = $(window);
|
||||
var elements = [];
|
||||
var elementsInView = [];
|
||||
var isSpying = false;
|
||||
var ticks = 0;
|
||||
var offset = {
|
||||
top : 0,
|
||||
right : 0,
|
||||
bottom : 0,
|
||||
left : 0,
|
||||
}
|
||||
|
||||
/**
|
||||
* Find elements that are within the boundary
|
||||
* @param {number} top
|
||||
* @param {number} right
|
||||
* @param {number} bottom
|
||||
* @param {number} left
|
||||
* @return {jQuery} A collection of elements
|
||||
*/
|
||||
function findElements(top, right, bottom, left) {
|
||||
var hits = $();
|
||||
$.each(elements, function(i, element) {
|
||||
var elTop = element.offset().top,
|
||||
elLeft = element.offset().left,
|
||||
elRight = elLeft + element.width(),
|
||||
elBottom = elTop + element.height();
|
||||
|
||||
var isIntersect = !(elLeft > right ||
|
||||
elRight < left ||
|
||||
elTop > bottom ||
|
||||
elBottom < top);
|
||||
|
||||
if (isIntersect) {
|
||||
hits.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user scrolls the window
|
||||
*/
|
||||
function onScroll() {
|
||||
// unique tick id
|
||||
++ticks;
|
||||
|
||||
// viewport rectangle
|
||||
var top = jWindow.scrollTop(),
|
||||
left = jWindow.scrollLeft(),
|
||||
right = left + jWindow.width(),
|
||||
bottom = top + jWindow.height();
|
||||
|
||||
// determine which elements are in view
|
||||
var intersections = findElements(top+offset.top, right+offset.right, bottom+offset.bottom, left+offset.left);
|
||||
$.each(intersections, function(i, element) {
|
||||
var lastTick = element.data('scrollSpy:ticks');
|
||||
if (typeof lastTick != 'number') {
|
||||
// entered into view
|
||||
element.triggerHandler('scrollSpy:enter');
|
||||
}
|
||||
|
||||
// update tick id
|
||||
element.data('scrollSpy:ticks', ticks);
|
||||
});
|
||||
|
||||
// determine which elements are no longer in view
|
||||
$.each(elementsInView, function(i, element) {
|
||||
var lastTick = element.data('scrollSpy:ticks');
|
||||
if (typeof lastTick == 'number' && lastTick !== ticks) {
|
||||
// exited from view
|
||||
element.triggerHandler('scrollSpy:exit');
|
||||
element.data('scrollSpy:ticks', null);
|
||||
}
|
||||
});
|
||||
|
||||
// remember elements in view for next tick
|
||||
elementsInView = intersections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when window is resized
|
||||
*/
|
||||
function onWinSize() {
|
||||
jWindow.trigger('scrollSpy:winSize');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time in ms
|
||||
* @license https://raw.github.com/jashkenas/underscore/master/LICENSE
|
||||
* @type {function}
|
||||
* @return {number}
|
||||
*/
|
||||
var getTime = (Date.now || function () {
|
||||
return new Date().getTime();
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a function, that, when invoked, will only be triggered at most once
|
||||
* during a given window of time. Normally, the throttled function will run
|
||||
* as much as it can, without ever going more than once per `wait` duration;
|
||||
* but if you'd like to disable the execution on the leading edge, pass
|
||||
* `{leading: false}`. To disable execution on the trailing edge, ditto.
|
||||
* @license https://raw.github.com/jashkenas/underscore/master/LICENSE
|
||||
* @param {function} func
|
||||
* @param {number} wait
|
||||
* @param {Object=} options
|
||||
* @returns {Function}
|
||||
*/
|
||||
function throttle(func, wait, options) {
|
||||
var context, args, result;
|
||||
var timeout = null;
|
||||
var previous = 0;
|
||||
options || (options = {});
|
||||
var later = function () {
|
||||
previous = options.leading === false ? 0 : getTime();
|
||||
timeout = null;
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
};
|
||||
return function () {
|
||||
var now = getTime();
|
||||
if (!previous && options.leading === false) previous = now;
|
||||
var remaining = wait - (now - previous);
|
||||
context = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
previous = now;
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
} else if (!timeout && options.trailing !== false) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables ScrollSpy using a selector
|
||||
* @param {jQuery|string} selector The elements collection, or a selector
|
||||
* @param {Object=} options Optional.
|
||||
throttle : number -> scrollspy throttling. Default: 100 ms
|
||||
offsetTop : number -> offset from top. Default: 0
|
||||
offsetRight : number -> offset from right. Default: 0
|
||||
offsetBottom : number -> offset from bottom. Default: 0
|
||||
offsetLeft : number -> offset from left. Default: 0
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
$.scrollSpy = function(selector, options) {
|
||||
selector = $(selector);
|
||||
selector.each(function(i, element) {
|
||||
elements.push($(element));
|
||||
});
|
||||
options = options || {
|
||||
throttle: 100
|
||||
};
|
||||
|
||||
offset.top = options.offsetTop || 0;
|
||||
offset.right = options.offsetRight || 0;
|
||||
offset.bottom = options.offsetBottom || 0;
|
||||
offset.left = options.offsetLeft || 0;
|
||||
|
||||
var throttledScroll = throttle(onScroll, options.throttle || 100);
|
||||
var readyScroll = function(){
|
||||
$(document).ready(throttledScroll);
|
||||
};
|
||||
|
||||
if (!isSpying) {
|
||||
jWindow.on('scroll', readyScroll);
|
||||
jWindow.on('resize', readyScroll);
|
||||
isSpying = true;
|
||||
}
|
||||
|
||||
// perform a scan once, after current execution context, and after dom is ready
|
||||
setTimeout(readyScroll, 0);
|
||||
|
||||
return selector;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for window resize events
|
||||
* @param {Object=} options Optional. Set { throttle: number } to change throttling. Default: 100 ms
|
||||
* @returns {jQuery} $(window)
|
||||
*/
|
||||
$.winSizeSpy = function(options) {
|
||||
$.winSizeSpy = function() { return jWindow; }; // lock from multiple calls
|
||||
options = options || {
|
||||
throttle: 100
|
||||
};
|
||||
return jWindow.on('resize', throttle(onWinSize, options.throttle || 100));
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables ScrollSpy on a collection of elements
|
||||
* e.g. $('.scrollSpy').scrollSpy()
|
||||
* @param {Object=} options Optional.
|
||||
throttle : number -> scrollspy throttling. Default: 100 ms
|
||||
offsetTop : number -> offset from top. Default: 0
|
||||
offsetRight : number -> offset from right. Default: 0
|
||||
offsetBottom : number -> offset from bottom. Default: 0
|
||||
offsetLeft : number -> offset from left. Default: 0
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
$.fn.scrollSpy = function(options) {
|
||||
return $.scrollSpy($(this), options);
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user