function follow_scroll_smoothly(elementSelector, footerSelector) {
var element = $(elementSelector);
var elementHeight = element.outerHeight();
var windowHeight = $(window).height();
var documentHeight = $(document).height();
var headerMinimumTop = element.offset().top;
var footerMaximumTop = documentHeight;
if (footerSelector && $(footerSelector).length) {
footerMaximumTop = documentHeight - $(footerSelector).outerHeight();
}
console.log("follow_scroll_smoothly headerMinimumTop=" + headerMinimumTop);
// Space between element and top/bottom of screen (when scrolling)
var topMargin = 5;
var bottomMargin = 15;
// Should probably be set in CSS; but here just for emphasis
element.css('position', 'relative');
function checkRangeRelativeTop(relativeTop) {
var result = relativeTop;
if (result < 0) {
// can not go above initial position
result = 0;
} else if (result > footerMaximumTop - elementHeight - headerMinimumTop) {
// can not go bewond footer
result = footerMaximumTop - elementHeight - headerMinimumTop;
}
return result;
}
function stickTop(scrollTop) {
var newRelativeTop = checkRangeRelativeTop(scrollTop - headerMinimumTop + topMargin);
element.stop(false, false).animate({
top: newRelativeTop,
}, 300);
$('#debug').prepend('<br>stickTop ' + newRelativeTop);
}
function stickBottom(scrollTop) {
// we don't use animate bottom since it is relative to element, we use top
var newRelativeTop = checkRangeRelativeTop(scrollTop - headerMinimumTop + windowHeight - elementHeight - bottomMargin);
element.stop(false, false).animate({
top: newRelativeTop,
}, 300);
$('#debug').prepend('<br>stickBottom ' + newRelativeTop);
}
var lastScrollTop = 0;
$(window).on('scroll', function(event) {
// get the vertical position of scrollbar
var scrollTop = $(window).scrollTop();
var scrollDownDirection = scrollTop > lastScrollTop;
lastScrollTop = scrollTop;
var elementTop = element.offset().top
var elementBottom = elementTop + elementHeight;
var weSeeBottom = windowHeight + scrollTop > elementBottom && scrollTop < elementBottom;
var weSeeTop = windowHeight + scrollTop > elementTop && scrollTop < elementTop;
$('#info').html(JSON.stringify(
{
windowHeight: windowHeight,
headerMinimumTop: headerMinimumTop,
elementHeight: elementHeight,
elementTop: elementTop,
footerMaximumTop: footerMaximumTop,
// scrollTop: scrollTop,
// weSeeBottom: weSeeBottom,
// weSeeTop: weSeeTop,
// scrollDownDirection: scrollDownDirection,
}
));
stickTop(scrollTop);
});
}
$(function() {
follow_scroll_smoothly('.sidebar','.footer');
follow_scroll_smoothly('.big-sidebar','.footer');
});
<div style="position: fixed; top:10px;right:50px;z-index=99999999;background-color: white;" id="info"></div>
<div style="position: fixed; top:30px;right:50px;z-index=999;background-color: white;" id="debug"></div>
<h1 class="header">
Header
</h1>
<div class="main">
<div class="sidebar">
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
</div>
<div class="content">
<p>Try to scroll to see how
<br>
<span style="background-color:yellow">small sidebar</span>
and
<span style="background-color:blue">big sidebar</span>
follows you.
</p>
<p class="big-paragraph">Paragraph 1</p>
<p class="big-paragraph">Paragraph 2</p>
<p class="big-paragraph">Paragraph 3</p>
</div>
<div class="big-sidebar">
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
</div>
</div>
<footer class="footer">
Some footer
</footer>
.sidebar {
float: left;
background: yellow;
}
.big-sidebar {
float: right;
background: blue;
}
.big-sidebar li {
padding-top: 200px;
}
.content {
float: left;
padding: 10px;
}
.main::after {
content: "";
display: table;
clear: both;
}
.big-paragraph {
padding-top: 300px;
}
.header,
.footer {
text-align: center;
}
External resources loaded into this fiddle: