How to scroll the page smoothly? - javascript

I need to scroll the page on load to a certain position on the page in an animated manner. And it works fine (using jQuery's animate):
$(document).ready(function () {
$('html, body').animate({
scrollTop: $('#today').offset().top - 50
}, 800, "linear");
});
However, one thing that it is not is smooth. Particularly on a mobile device it feels very jerky.
I've seen some CSS animations (using transition and transform) that are very smooth but can't figure out how to apply it to page scrolling. Is it possible to do what I want using CSS?

Try setting the following css:
<style type="text/css">
html {
scroll-behavior: smooth !important;
}
</style>
You could also try using vanilla JavaScript instead of jQuery:
function showIt(el_id) {
var el = document.getElementById(el_id);
el.scrollIntoView(true, {behavior: "smooth"});
}
showIt('today')
Also consider adding an itermediary element in the middle of the page, example:
<div id="middle" style="display: none;"></div>
function showIt(el_id) {
var middle_el = document.getElementById('middle');
var el = document.getElementById(el_id);
middle_el.scrollIntoView(true, {behavior: "instant"}); // Go to middle directly and then scroll slowly to #today.
el.scrollIntoView(true, {behavior: "smooth"});
}
showIt('today')
More info on scrollIntoView: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
Also you might be interest in: Scrolling slow on mobile/ios when using overflow:Scroll if you are on iOS

Try using easeOutCubic or another easing function (choose one here):
$(document).ready(function () {
$('html, body').animate({
scrollTop: $('#today').offset().top - 50
}, 800, "easeOutCubic");
});

The best solution for cross-browser/cross-device decent smooth scrollTop I found is using velocity.js. It's faster than jQuery's animate, quite light and supports multiple syntaxes, one of them being the one used by jQuery.animate(), so all you need to do is to replace .animate() with .velocity() (and loading the thing, of course).
I know there might be other solutions out there, but this one has been solid for years, they are always keeping it up to date, I'd say it's a keeper in any serious frontend web developer's tools. You'll find some very fancy names on velocity's libscore page. And no, I'm not affiliated. I'm just thankful for being able to use it for free.

Related

scrollTo() and jquery scrollTop() not working in Chrome

I am making a puzzle platformer using html elements as obstacles. Some levels I want to start at the bottom of the document. So I tried using
$(document).ready(function() {
$(document).scrollTop($(document).height() - $(window).height());
}
and
$(document).ready(function() {
window.scrollTo(0, document.body.scrollHeight);
}
they both work perfectly fine in Firefox but neither work in Chrome. I only could get it to work when animating the scroll which will not work for what I am trying to do.
edit: I am not using jQuery.toScroll() I tried vanilla toScroll and jQuery.scrollTop()
Browsers attach their scrollbars to the page differently. Use $('html,body').scrollTop(). You should also use $(window).innerHeight(). Like this...
$('html,body').scrollTop($(document).height() - $(window).innerHeight());
//Or to smooth scroll it
$('html,body').animate({
scrollTop: $(document).height() - $(window).innerHeight()
}, 1000);
Note: $(document).height() will return the same value as $(window).innerHeight() if the document is shorter than the window.
I did not execute full testing but I'm also experiencing the issue via Developer Tools at least on a specific jQuery version and latest Chrome. animate does work. scrollTop and scrollTo do both not work. Nonetheless even if you explicitly said that you not want to use animate I still recommend it as it is working and I see no reason of not using it if you use it like this:
$('html, body').animate({scrollTop: 500}, 0);
This will scroll to 500px and has no animation at all because the duration is set to 0. I also have to point on a previous issue with Chrome:
Possible duplicate
Issue on Github
I know the issue is closed but it might be relevant and probably it should be reported because there is something fishy.

smooth scroll does not work with overflow-y

I am trying to use a smooth scroll and adopted an example I found online. Here is a fiddle with my code
https://jsfiddle.net/4DcNH/144/
I have special conditions set to html and body (basically to offset the page context by 50px from the top to avoid the navbar). Therefore the smooth scroll does not work. Does anybody know a solution to this?
thanks
carl
$(document).ready(function() {
$('a[rel="relativeanchor"]').click(function(){
$('html, body').animate({
scrollTop: $( $.attr(this, 'href') ).offset().top
}, 2000);
return false;
});
});
Is this what you're after?
$(document).ready(function () {
if(!/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())){
$('html').css({'overflow-x':'auto','overflow-y':'hidden'});
}
$('a[rel="relativeanchor"]').click(function () {
var $el = $($(this).attr('href'));
$('html, body').stop().animate({
scrollTop: $el.prop('offsetTop')
}, 2000);
return false;
});
});
JSFiddle
Updates were needed in the CSS. The html overflows were removed for chrome, because otherwise, this would not work in Chrome. However, the overflows are needed for Firefox, so they are done by setting it dynamically in the JavaScript (set if not chrome).
If you want to maintain an offset, subtract it from the calculated offset. Given the above, $el.prop('offsetTop') - 50 adds 50px above.
The issue appears to be related to differences in how Chrome scrolls the <body> with height:100%. A discussion of the issue is here: Body set to overflow-y:hidden but page is still scrollable in Chrome
A workable solution is to wrap the scrolling content in <div class="content"> and disable scrolling on <body>.
Here's a JSFiddle to demonstrate the updated behavior: https://jsfiddle.net/f1zv1c5k/5/
To get the scroll to stop at the appropriate point, you need to subtract the vertical offset applied to the <html> tag (using $el.prop('offsetTop') recommended by #vol7ron) when scrolling. Your smooth scroll function would look like this:
$('a[rel="relativeanchor"]').click(function(){
var $el = $($(this).attr('href'));
$('.content').animate({
scrollTop: $el.prop('offsetTop')
}, 2000);
return false;
});

Scroll to link with skrollr

I am using https://github.com/Prinzhorn/skrollr to animate the background of my site as I scroll. However I am also wanting to have my links scroll up and down the page like a normal single page site would do.
The problem is that both are working if I manually scroll the background changes, if I click the link the page scrolls to the correct place. The problem is that when I click the button the background doesn't scroll as well.
It seems like I am working with two different scroll functions and as a result they aren't working together and I need to use the same one.
Here is the code.
js - Scroll to link:
var $root = $('html, body');
$('a').click(function() {
var href = $.attr(this, 'href');
$root.animate({
scrollTop: $(href).offset().top
}, 500, function () {
window.location.hash = href;
});
return false;
});
js – Skrollr init
skrollr.init({
smoothScrolling: true,
forceHeight: true
});
I will try put together a fiddle to make it more clear but hopefully the answer is really simple.
If anyone else ever faces this problem the answer lies her: https://github.com/Prinzhorn/skrollr-menu
This will allow you to scroll to you internal links along with Skrollr animations. A HUGE plus and a very simple fix, you don't even need any of your own scrolling code just this and it will work with you links.
There's a way to do this, Skrollr has some methods very useful, in console, just type the variable contains skrollr, it will show some methods that you can use, one of them is "setScrollTop(int, bool)", so just call this method with the info you need, for example:
s.setScrollTop(9000, true)
Which means that I want it to scroll to the height position 9000. It works fine, you just need to know the height position where you need to go.

$(window).scroll in vanilla JavaScript

What's the equivalent of the following in plain JS?
$(window).scroll(function() { });
I'm also looking to animate scroll, e.g.:
$('html, body').animate({scrollTop:1750}, 'slow');
Should I be using requestAnimationFrame?
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
Are there any examples that trigger an animation once on click and not continuous renders?
Question 1
window.onscroll = function() {
console.log('scrolling');
};
or if your targeted browsers support addEventListener :
window.addEventListener('scroll', function() {
console.log('scrolling');
});
Question 2
In my opinion, if you're just scrolling from one section to a another section of your page, and not having some sort of constantly running scrolling movement, you're fine doing this without using requestAnimationFrame.
You can find good implementations of scrolling to a particular part of the window in pure javascript, I suggest checking out their source(or even using them).

How can I "snap" scroll to the nearest predefined position?

I have a website that is essentially four divs - each of which is set to the height of the window so that the total document is four times the height of the window.
The idea is that a click on a div advances the scroll by one "window height" - which works fine, like this:
// on click event
if(cur_frame<number_slides){
scrolling = true;
$('html,body').animate({scrollTop:window_height*cur_frame},function(){
scrolling=false;
});
}
After the user scrolls the page manually, however, I'd like to "snap" the position to the nearest multiple of the window height - so a given div is once again centered on the screen. I tried using a timeout, figuring that a small delay would keep it from triggering a thousand times a second...
// on scroll event
clearTimeout(scroll_timer);
if(!scrolling) scroll_timer = setTimeout(function(){
if(cur_scroll!=window_height*(cur_frame-1)) {
scrolling = true;
$('html,body').stop().animate({scrollTop:window_height*(cur_frame-1)},function(){
scrolling = false;
});
}
},100); //20? 400? 1000?
...but couldn't strike a balance between the script fighting the user over scroll position, or a seriously long delay that defeats the "snapping" effect.
Any suggestions how this might be achieved?
There is a CSS spec for this, and it is well supported with native rendering and very nice touch behavior except on Chrome: http://caniuse.com/#feat=css-snappoints
For the laggard browser, there's a polypill: https://github.com/ckrack/scrollsnap-polyfill
See also How to emulate CSS Scroll Snap Points in Chrome?
The jquery scrollsnap plugin for this supports down to IE9.
What you're looking for is called "Scroll Snap".
<script src="demo/foundation/javascripts/jquery.js"></script>
<script src="src/jquery.event.special.js"></script>
<script src="src/jquery.easing.min.js"></script>
<script src="src/jquery.scrollsnap.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(document).scrollsnap({
snaps: '.snap',
proximity: 50
});
});
</script>
What about using a simple scrollTo? Plain Javascript and CSS used, no frameworks or libraries.
Here are two examples, one for vertical scrolling and the other for horizontal scrolling:
Vertical: https://jsfiddle.net/x9z5tpye/
Horizontal: https://jsfiddle.net/bwsyn6q4/
If you want to consider a cross-browser javascript re-implementation of the native CSS Scroll Snap spec, as already answered here: How to emulate CSS Scroll Snap Points in Chrome?, you can use
this library:
The main reason to use this instead of the native css solution is that it works in all modern browsers and has a customizable configuration to allow custom timing in transitions and scrolling detection.
The library re-implements the css snapping feature using vanilla javascript easing functions, and works using the values of the container element's scrollTop/scrollLeft properties and the scroll Event Listener
Here is an example that shows how to use it:
import createScrollSnap from 'scroll-snap'
const element = document.getElementById('container')
const { bind, unbind } = createScrollSnap(element, {
snapDestinationX: '0%',
snapDestinationY: '90%',
timeout: 100,
duration: 300,
threshold: 0.2,
snapStop: false,
easing: easeInOutQuad,
}, () => console.log('snapped'))
// remove the listener
// unbind();
// re-instantiate the listener
// bind();
You could do this with javascript or for a slightly simpler and older solution you can use page anchors.
If you change your document.location.hash to an anchor that exists in the page then the browser will scroll to it.
So in your HTML put some anchors in the page:
<a name="anchor1" id="anchor1"></a>
then in your js put:
document.location.hash = "anchor1";

Categories

Resources