-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathjquery.scrollomatic.js
112 lines (89 loc) · 4.16 KB
/
jquery.scrollomatic.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
;(function($) {
$.fn.scrollomatic = function(options) {
var o = $.extend({
offset: 0, // fixed offset to always apply (good for accommodating fixed headers)
duration: 500, // scroll duration in milliseconds
after: function () {} // invoked after scrolling complete. arguments: hash (as defined on the link). context will be the clicked link.
}, options);
var win = $(window),
isAutoscrolling = false;
function getOffset() {
return typeof(o.offset) === 'function' ? o.offset() : o.offset;
}
function setUrlHash(hash) {
if ('replaceState' in window.history && window.location.hash !== hash) window.history.replaceState('', '', hash);
}
function unsetUrlHash() {
if ('replaceState' in window.history) window.history.replaceState('', document.title, window.location.pathname + window.location.search);
}
function scrollToHashTarget(hash, duration, suppressCallbacks) {
var target = $(hash);
if (target.length === 0) return;
var targetOffsetTop = target.offset().top,
targetMarginTop = parseInt(target.css('margin-top')),
offset = getOffset(),
scrollTop = targetOffsetTop - (offset + (isNaN(targetMarginTop) ? 0 : targetMarginTop));
isAutoscrolling = true;
$('html, body').stop().animate({
scrollTop: scrollTop
}, duration, 'swing', $.proxy(function () {
isAutoscrolling = false;
setUrlHash(hash);
if (o.after && !suppressCallbacks) o.after.call(this, hash);
}, this));
}
var targets = [];
this.each(function() {
var $this = $(this);
var hash = $this.attr('href');
if (!hash || hash.length === 0 || !hash.charAt(0) === '#') return;
var target = $(hash);
if (target.length === 0) return;
targets.push(target);
$this.click(function(e) {
e.preventDefault();
scrollToHashTarget(hash, o.duration);
});
});
// When scrolling, if we're showing one of the known targets in the viewport, update the
// URL fragment to reflect that. If multiple targets are visible, show the topmost one.
// If none are visible or the page is scrolled to the top, unset the fragment.
function onScroll () {
if (isAutoscrolling) return;
var topmost,
topmostY = 0,
offset = getOffset(),
scrollTop = win.scrollTop();
if (scrollTop == 0/* || scrollTop < offset*/) {
unsetUrlHash();
return;
}
var windowHeight = win.height();
for(var i = 0; i < targets.length; i++) {
var target = targets[i],
outerHeight = target.outerHeight(),
top = target.offset().top,
bottom = top + outerHeight,
windowTop = scrollTop + offset,
windowBottom = windowTop + windowHeight;
var isInViewport = !(windowBottom < top || windowTop > bottom);
if (isInViewport && (!topmost || top < topmostY)) {
topmost = target;
topmostY = top;
}
}
topmost ? setUrlHash('#' + topmost.attr('id')) : unsetUrlHash();
};
$(function () {
setTimeout(function () {
// When the page loads with a fragment, it doesn't account for any fixed headers, resulting
// in the page scrolling too far. After a slight delay, explicitly scroll to the target.
if (window.location.hash) scrollToHashTarget(window.location.hash, 0, true);
// Wait a bit to bind the scroll handler in case the page was loaded with a fragment. We don't
// want to handle those scroll events.
win.scroll(onScroll);
}, 200);
});
return this;
};
}(jQuery));