I have 1 fixed DIV #scroll-up
that is 100% height of the browser window. I have another DIV #next-prev
that sits underneath it. As soon as #next-prev
comes into view I want the #scroll-up
DIV to begin scrolling up. My code blow does this, however as soon as #next-prev
is in view #scroll-up
just jumps up for no reason. The effect should be seamless.
How do I prevent the jump?
I have setup a demo of the problem on jsFiddle here: http://jsfiddle.net/sya0jr6p/1/
JS
Uses the jQuery Visible plugin to detect when #next-prev is in view.
$(window).scroll(function() {
if ($('#next-prev').visible(true)) {
console.log ( '#next-prev visible' );
// Initiate scroll up of '#scroll-up' when '#next-prev' is in the viewport
var $tagline = $('#scroll-up');
var windowScroll;
// Function
function slidingTitle() {
//Get scroll position of window
windowScroll = $(this).scrollTop();
$tagline.css({
'top' : -(windowScroll/3)+"px"
});
}
// Initiate
slidingTitle();
} else {
console.log ( '#next-prev not visible' );
// The element is NOT visible, do something else
}
});
UPDATE
After further testing the jump ONLY exists when #nav-wrap
isn't already on the screen. If it is on the screen then the jump doesn't occur and works as expected.
Another thing I noticed; When you first load the page AND #nav-wrap
isn't already on the screen. Scroll down the page as normal until you see #scroll-up
jump up and if you continue scrolling down you'll see what I want to happen. #scroll-up
scrolling normally. BUT if after the jump has taken place you scroll back up the page to the very top, you'll notice the position of #scroll-up
isn't the same as when you first loaded the page. Could the calculation wrong? or could the order of the code be wrong? Just a few ideas...
UPDATE
This is a animated GIF of how I want the scroll to work, the animate answers that have been given aren't intuitive:
The reason you see the jump is that you simply divide the current window scroll position once the #next-prev
comes into view by 3 and set that as a top value for #scroll-up
... the window scroll when you reach #next-prev
will be larger than 0, hence the scroll divided by three will also be larger than zero, and #scroll-up
will immediately get a (large) negative top
value.
I don't know where you got the division by three from - that would create a slower scrolling of the element (aka. parallax effect) - which means that even if there was no jump and #scroll-up
started to scroll at the right point, it would still cover two thirds of #next-prev
once #next-prev
was fully in view... So I don't think you actually want the parallax effect...
What I think you want is:
The top
value of #scroll-up
needs to be zero until the first pixel of #next-prev
scrolls into view, and then grow gradually as #next-prev
comes "more" into view, making sure it stays exactly above #next-prev
... ie. the top of #scroll-up
needs to be 0 minus the number of pixels #next-prev
's top is from the bottom of the viewport - and of course always zero if #next-prev
is still completely below the viewport.
Here's a way to achieve that...
(function($) {
$(window).scroll(function() {
//Get the footer position relative to the document
var footer_offset = $("#footer").position().top;
//Get the window height (viewport height)
var window_height = $(window).height();
//Get the current scroll position
var window_scroll = $(window).scrollTop();
//Calculate the bottom of the window relative to the document
var window_bottom_offset = window_scroll + window_height;
//Calculate "how much" of the footer is above the bottom of the window
var footer_visible_pixels = window_bottom_offset - footer_offset;
//Default #semistuck's position to top: 0px
var semistuck_top = 0;
//But if the footer is above the bottom of the screen...
if (footer_visible_pixels > 0) {
//...move #semistuck up by the same amount of pixels
semistuck_top = -footer_visible_pixels;
}
//Actually move it
$("#semistuck").css('top', semistuck_top + 'px');
//Just to see each step of the calculation
console.log(footer_offset, window_height, window_scroll, window_bottom_offset, footer_visible_pixels, semistuck_top);
});
}(jQuery));
body,
html {
margin: 0;
}
#semistuck,
#loooooong,
#footer {
margin: 0;
padding: 4em 1em;
text-align: center;
box-sizing: border-box;
}
#semistuck {
position: fixed;
width: 50%;
height: 100%;
top: 0;
left: 0;
background: #afa;
}
#loooooong {
height: 2000px;
margin-left: 50%;
width: 50%;
background: repeating-linear-gradient(45deg, #606dbc, #606dbc 100px, #465298 100px, #465298 200px);
}
#footer {
background: #faa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="semistuck">
This is stuck until the<br>footer scrolls into view
</div>
<div id="loooooong"></div>
<div id="footer">
When this gets into<br>view #semistuck will<br>get out of its way
</div>
Obviously the above code is way more verbose than needed, but I felt that very small increments in the calculation makes it clearer what happens....
Here's the same thing in a shorter form:
var footer_visible_pixels = $(window).scrollTop() + $(window).height() - $("#footer").position().top;
$("#semistuck").css('top', (footer_visible_pixels > 0 ? -footer_visible_pixels : 0) + 'px');
...come to think of it you may actually want the parallax effect, but just want the footer to be on top, so here's a slightly modified version which gives a somewhat cool effect:
(function($) {
$(window).scroll(function() {
//Get the footer position relative to the document
var footer_offset = $("#footer").position().top;
//Get the window height (viewport height)
var window_height = $(window).height();
//Get the current scroll position
var window_scroll = $(window).scrollTop();
//Calculate the bottom of the window relative to the document
var window_bottom_offset = window_scroll + window_height;
//Calculate "how much" of the footer is above the bottom of the window
var footer_visible_pixels = window_bottom_offset - footer_offset;
//Default #semistuck's position to top: 0px
var semistuck_top = 0;
//But if the footer is above the bottom of the screen...
if (footer_visible_pixels > 0) {
//...move #semistuck up by the same amount of pixels
semistuck_top = -footer_visible_pixels/3;
}
//Actually move it
$("#semistuck").css('top', semistuck_top + 'px');
//Just to see each step of the calculation
console.log(footer_offset, window_height, window_scroll, window_bottom_offset, footer_visible_pixels, semistuck_top);
});
}(jQuery));
body,
html {
margin: 0;
}
#semistuck,
#loooooong,
#footer {
margin: 0;
padding: 4em 1em;
text-align: center;
box-sizing: border-box;
}
#semistuck {
position: fixed;
width: 50%;
height: 100%;
top: 0;
left: 0;
background: #afa;
}
#loooooong {
height: 2000px;
margin-left: 50%;
width: 50%;
background: repeating-linear-gradient(45deg, #606dbc, #606dbc 100px, #465298 100px, #465298 200px);
}
#footer {
background: #faa;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="semistuck">
This is stuck until the<br>footer scrolls into view
</div>
<div id="loooooong"></div>
<div id="footer">
When this gets into<br>view #semistuck will<br>get out of its way
</div>
Only two things are changed compared to the original suggestion:
position: relative
to the footertop
of #semistuck
by threeNone of this will work on iPhones, iPads, and probably most other touch devices. They simply don't fire any scroll events during scroll, but only after. So if support for mobile devices is a concern, you should detect those and provide a fallback - the alternative is a very jumpy effect that happens only after scrolling ends. To the best of my knowledge there is no workaround, and depending on the scenario the best way to handle it may be to:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With