Don't block scrolling animation when setting scroll programmatically via jQuery - javascript

Here's a full codepen of the situation I will describe in detail below: https://codepen.io/Adam0410/full/MGXjaz/
The javascript contained in the codepen (the core of the issue) is below:
var collapsed = false;
$(window).scroll(function(){
var scroll = window.pageYOffset || document.documentElement.scrollTop;
if (scroll > 207 && !collapsed) {
$("#header").css({
position: "fixed",
height: "50px",
"line-height": "50px"
});
$("#content").css("margin-top", "207px");
$(document).scrollTop(scroll - 50);
collapsed = true;
} else if (scroll < 155 && collapsed) {
$("#header").css({
position: "static",
height: "257px",
"line-height": "257px"
});
$("#content").css("margin-top", "0");
$(document).scrollTop(scroll + 50);
collapsed = false;
}
});
I am attempting to make a large header that is part of the flow of the document, that then turns into a smaller fixed header as you scroll down past it. I want the action of the user scrolling to be smooth during this process.
If you view the pen on mobile (or use chrome's device toolbar) with smooth scrolling and scroll slowly around the breakpoint where the header changes you can see it's completely smooth.
However if you view it on a desktop (with chrome again, or any other browser) the scrolling with a scroll wheel is done in 100-pixel increments. For this reason once again if you scroll around the breakpoint where the header changes you can see it is not smooth.
This occurs since the 100-pixel scrolling doesn't occur instantly and in the process of changing the scrollTop of the document the 100-pixel scrolling animation gets canceled. Is there any way to detect and resume this scrolling animation once I've set the scrollTop property?

Please check https://codepen.io/anon/pen/PeabEL
Changed the js to
$(window).scroll(function(){
var scroll = window.pageYOffset || document.documentElement.scrollTop;
var newHeight = 50;
if(257-scroll>50)
newHeight = 257-scroll;
$("#header").css({
position: "fixed",
height: newHeight+"px",
"line-height": newHeight+"px",
});
});
also added
#content {
...
margin-top:257px;
}
and,
#header {
...
position: fixed;
}

I have another approach with the help of a very small plugin called smoothwheel (Get it here). I extended the answer of #Rohith Murali and created an example. Have a look here:
https://codepen.io/anon/pen/NMBdpx
The plugin itself enables you to scroll smoothly with low impact on performance. Does this fit your needs?

Related

Hide/Show Nav on Vertical Scroll

I've implemented a scroll based state change on my nav bar, which fades in a fixed position nav at the screen top after the user scrolls down a lengthy page and then hides it and puts it back in it's relative position again as they scroll back up to see the top of the page. It works great 90% of the time. Here's my code..
function scrollNavReveal() {
var nav = $('nav.nav-primary');
$(window).scroll(function(){
var scrollpos = $(this).scrollTop();
if ((scrollpos > 200) && (scrollpos < 800)) {
nav.fadeOut(500);
}
else if (scrollpos > 800) {
nav.css({"position": "fixed", "width": "100%"}).fadeIn(500);
}
else {
nav.css({"position": "relative", "width": "100%", "display": "block"});
}
});
}
The issue is that when I scroll to the page top very quickly with my mouse, sometimes the nav does not appear. I have to make a small scroll on the mouse and then it pops back into place. This also happens when I use my scroll to top function which is initiated when a user clicks the 'go to top' button in the footer.
function go_to_top() {
$('a.naada-top').click(function() {
$('html, body').animate({scrollTop:0}, 'slow');
})
}
In the go_to_top function I believe the .animate({scrollTop:0}, 'slow'); does not take into account the height of the nav itself, which isn't currently in position: relative; (hence not in the normal DOM flow/scroll height).
Both issues seem very similar, but slightly different and they can be seen and tested here naada.staging.wpengine.com
Thanks for your help!
I recommend adding an if statement for < 200 rather than an else at the end of the statement. If you don't have to roll through each if statement and prove them false the processing speed will be quicker, which might catch the quicker scroll a little better. I also use the (document).scroll(function(){ instead of window... but it should work either way.
Here's how I would code this:
$(document).scroll(function() {
var scroll_dst = ($(document).scrollTop());
if (scroll_dst < 200){
nav.css({"position": "relative", "width": "100%", "display": "block"});
}else if((scroll_dst >= 200) && (scroll_dst < 800)) {
nav.fadeOut(500);
}else if((scroll_dst >= 800){
nav.css({"position": "fixed", "width": "100%"}).fadeIn(500);
}
});
You could also make it so when you click the 'go to top' function it automatically places the nav.css back to it's relative state. It's not a perfect solution, but it's better than having no nav at all.
Apparently functions attached to scroll events can be very resource heavy and I think the biggest issue with my code is that the browser couldn't evaluate each of my if statements for every pixel that was scrolled, especially when you scrolled fast to the page top. So I've found a more elegant solution.
In this solution the scroll information is stored in a boolean and he uses a setInterval to check on the scroll position. It's not exactly the same interactivity that I had before, but based on the circumstances it seems the most elegant and resource friendly option. It's actually really pretty code..
You can see it live here.

Page noscroll keeping scrollTop position in sticky footer layout

I can't handle such a mind-breaker:
It's common approach to hide page scroll when a tall popup shows. But when you set overflow: hidden to html and body elements, the content automatically returns to it's top (scrollTop: 0). It's no problem to keep scrollTop position, and reset it on popup's disappearing. But if you use transparent mask, user will see unnecessary jump from current scroll position to the top. How to escape this?
In the current Chrome and Firefox, I can set overflow: hidden only to html element to reach what I want, but it's not working on mobile devices.
Maybe someone can propose a good cross-browser solution.
I don't think this should be happening. I would look at the popup code to see if it's the culprit sending the page to the top and using subpar css to position the popup element.
In any case, here's code that ought to counter the behavior you're encountering. Since I cannot reproduce the problem, I cannot test my proposed fix. I think you'll find though that your pop up will be scrolled away out of view.
function keepScroll(){
var x = $('body').scrollLeft() + $(document.documentElement).scrollLeft();
var y = $('body').scrollTop() + $(document.documentElement).scrollTop();
$('html').css({
'overflow':'hidden'
});
$('body').css({
'overflow':'hidden'
});
$(window).scrollTop(y).scrollLeft(x);
}
The sicky footer layout has html and body height equal to '100%'. And when you set overflow:hidden it crops all the content and returns it to the top position.
To avoid this, you should set html and body height to 'auto' if scroll exists (you should check it to keep sticky footer behavior)
function keepScroll(){
var scrollHeight = $('body')[0].scrollHeight > $('html')[0].scrollHeight ? $('body')[0].scrollHeight : $('html')[0].scrollHeight,
keepCSS = scrollHeight > $(window).height() ? {'overflow':'hidden','height':'auto'} : {'overflow':'hidden'};
$('html, body').css(keepCSS);
}
See the fiddle, for live demo
Edit 1
This solution is still not working on mobile (overflow: hidden doesn't disable scrolling on iPad, the position 'fixed' fix for body throw the content to the top), so the issue is open
Edit 2
Find a fix, for mobiles. Maybe it isn't so clean, but works.
var scrollKeeper = (function() {
var scrollHeight = $('body')[0].scrollHeight > $('html')[0].scrollHeight ? $('body')[0].scrollHeight : $('html')[0].scrollHeight,
keepCSS = scrollHeight > $(window).height() ? {'overflow':'hidden','height':'auto'} : {'overflow':'hidden'},
scrollTop = 0;
return {
keep : function() {
scrollTop = $(window).scrollTop();
$('body').css({'position': 'fixed', 'width':'100%', 'top': -scrollTop + 'px'});
$('html, body').css(keepCSS);
},
release : function() {
$('html, body').removeAttr('style').scrollTop(scrollTop);
}
}
})();
Tip: Of course in the real development you should use css classes to avoid removeAttr(style) etc.
Tested on iPhone and Ipad (iOS 8+).
The fiddle http://jsfiddle.net/m1eav032/5/

Preventing elastic scrolling on OS X Chrome and Safari

I'm looking for a way to prevent the elastic scrolling that occurs on OS X in both Chrome and Safari.
If you don't understand what I mean, it's when you scroll up while the page is at the top, or down when the page is at the bottom, and it shows a gray background behind the page.
There is a css solution for single page apps where you just add overflow:hidden to the html or body
However, I need to be able to scroll.
I've come up with a solution using Javascript (JQuery), but it's only for scrolling passed the top, and only works for chrome. Also, it's a bit buggy in Safari.
$(window).on('scroll', function(e){
scrollAmount = $(this).scrollTop();
if(scrollAmount < 1){
$(this).scrollTop(1);
}
});
So that's just checking of the user scrolls below 1 meaning they try to scroll up passed where the page ends. I tried 0 but that didn't work.
I haven't been able to find a way to check if the user scrolls passed the bottom of the page.
So any thoughts?
Edit:
$(window).on('scroll', function(e){
scrollAmount = $(this).scrollTop();
if(scrollAmount < 1){
$(this).scrollTop(1);
}
if(scrollAmount > $(document).height() - $(window).height()){
$(this).scrollTop($(window).height());
}
});
Now I've added a check for if we scroll passed the bottom of the page. This method is not working though, it's bouncing around very ungracefully.
When you quickly scroll up to the top, the elastic browser causes the scroll top to become negative. Using the st <= 0, will make sure no action is taken when this happens.
$(window).on('scroll',function(){
var dh = $(document).height(),
wh = $(window).height(),
st = $(window).scrollTop();
if (st <= 0) {
$(this).scrollTop(0);
} else if (st + wh >= dh) {
$(this).scrollTop(dh);
}
});
You can now use overscroll-behavior:
html, body {
overscroll-behavior: none;
}
While I dislike the methodology, you can use jQuery to get the document height (and thus the bottom of the page for the scroll distance using $(document).height();)
Another method would be to wrap the entire page with a <div id="preventScroll"></div> wrapper, with the property overflow: scroll; height: 100%; width: 100%;
That would be a separate scrolling device and prevent your whole issue.

How to create a fixed / sticky sidebar in CSS / JS?

I'm trying to create a website with main content area and a sidebar, something like here on Stack Overflow. The goal is that when you scroll down, the sidebar stays visible.
I have seen two approaches to this:
position:fixed;
JavaScript manipulation with the DOM
Approach no. 1, as far as I know, will have a problem when the viewport is smaller than the sidebar contents so I guess that can't be used reliably and JavaScript scripts that I have seen are usually animated or generally "slow" (you can see that there is redrawing going on after each scroll).
Can someone point out a JavScript library / CSS approach that would not suffer from the aforementioned issues?
Edit: an example would be this page but with the sidebar sticking to the top without an animation and correctly handling the situation when the sidebar is higher than content / viewport.
I don't like heavy JS solutions, so important thing to ask is - preferred compatibility. In IE8+ it is possible instead of
var $window = $(window),
$sidebar = $(sidebar);
$window.on('resize', function(){
$sidebar.height($window.innerHeight());
});
$window.resize();
do something like this (pure CSS solution):
#sidebar {
position: fixed;
left: 0; /* or right */
top: 0;
bottom: 0;
overflow: auto;
}
When you have top&bottom / left&right value at the same time, box will be stretched. (JSFiddle demo)
Got it. It is Javascript based, but I'm sure that's nothing heavy and even IE8 should solve it pretty fine.
var top = $('#sidebar').offset().top;
var height = $('#sidebar').height();
var winHeight = $(window).height();
var gap = 10;
$(window).scroll(function(event) {
var scrollTop = $(this).scrollTop();
// sidebar reached the (end - viewport height)
if (scrollTop + winHeight >= top + height + gap) {
// if so, fix the sidebar and make sure that offset().top will not give us results which would cancel the fixation
$('#sidebar').addClass('fixed').css('top', winHeight - height - gap + 'px');
} else {
// otherwise remove it
$('#sidebar').removeClass('fixed').css('top', '0px');
}
});​
demo
You could catch client window's height and giving it to your sidebar like this :
var sidebarHeight = $(window).innerHeight();
$('#sidebar')​​​​​​​​​​​.css('height',sidebarHeight);​​​​​​​​​​​​​
With the proper CSS for the sidebar :
#sidebar {
position: fixed;
right: 0;
top: 0;
width: 200px;
overflow: hidden;
}
Here is a working JSFiddle.
You could also watch for window resizing to avoid a mess on resize :) Here is the way to go with jQuery
Good luck
Not sure if you have this figured out but I have created a contained sticky sidebar jQuery plugin. It's really simple and allows you to invoke with just one line of jQuery. Take a look here: http://mojotech.github.com/stickymojo/
It starts by position: fixed; then uses javascript to handle any resizes, scrolls and even allows you to specify a footer element that it should not intersect. By combining these approaches you will get a smooth looking fixed element. Plus, we made it easy for you.
Code and demo here: http://closure-library.googlecode.com/svn/docs/class_goog_ui_ScrollFloater.html

using a simple jquery script to have a div follow the page on scroll

I'm trying to use this script here: http://css-tricks.com/scrollfollow-sidebar/ to make a simple div that follows the window as the user scrolls. I changed it from 0 to topPadding and changed topPadding to topPadding*2 to get the right top offset.
Unfortunately this has the side effect of the object making the page a little longer and allowing the user to scroll infinitely. This bug is also actually in the original code without my larger toppadding changes if you make the window small enough.
I also tried another plugin for jquery, but it didn't work at all and this gives me what I need, so how can I fix it?
Why not just use CSS.
#theNonMovingDiv {position:absolute; position: fixed; top: Npx; right:Mpx}
position:fixed; doesn't work in ie6, but including the position:absolute; will give you a rough approximation.
I've knocked together this quick amendment, which limits based on the document height. I'm not certain that jQuery is giving an accurate height, hence a safety barrier of 100px. Even if the height isn't quite right, any extra scrolling will be limited and certainly not infinite.
<script type="text/javascript">
var documentHeight = 0;
var topPadding = 15;
$(function() {
var offset = $("#sidebar").offset();
documentHeight = $(document).height();
$(window).scroll(function() {
var sideBarHeight = $("#sidebar").height();
if ($(window).scrollTop() > offset.top) {
var newPosition = ($(window).scrollTop() - offset.top) + topPadding;
var maxPosition = documentHeight - (sideBarHeight + 100);
if (newPosition > maxPosition) {
newPosition = maxPosition;
}
$("#sidebar").stop().animate({
marginTop: newPosition
});
} else {
$("#sidebar").stop().animate({
marginTop: 0
});
};
});
});
</script>
I can duplicate your bug in my browser (Firefox 3.5).
The problem is that the code doesn't look to see if the bottom of the sidebar falls off the end of the document.
Your best bet is to use the .height() method to check. You can get the height of the sidebar (as presented in the example) as $("#sidebar").height(), and the height of the whole document as $(document).height().
What exactly the behavior should be -- up to you. It involves an extra if, to make sure all your pixels line up right, but design questions, like how the sidebar should align against the bottom, are going to be a matter of personal taste.

Categories

Resources