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/
Related
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?
I have a div in a div.
The first div has a unknown height. The second one has the height of 125px.
I want to make the second one a sticky div which is only sticky in this div.
The grey box is the container and the social media div next to it should be sticky.
After the container more content will come, so I cant use position: fixed. I tried to use position: absolute and change the top value or the transform: translate, but when I Do that Chrome is jittering around.
Code that I tried to use:
$offset = $(".social-media").offset().top;
$containerHeight = $(".sticky-container").height();
$bottom = $containerHeight + $(".sticky-container").offset().top;
$maxPoint = $containerHeight - $(".social-media").height();
$(window).scroll(function(){
if($(window).scrollTop() >= $offset){
if($(window).scrollTop() >= $bottom){
$(".social-media").css({transform: "translate(0px,"+$maxPoint+"px)"});
}else{
$scroll = $(window).scrollTop() - $offset;
$(".social-media").css({transform: "translate(0px,"+$scroll+"px)"});
}
}else{
$(".social-media").css({transform: "translate(0px,0px)"});
}
});
Since the jsbin you provided shows the solution works without jittering, the problem might lie in the repaints triggered by other elements of your site, not the code you pasted. Have a look at the Google's repaint optimization guidelines, it might help you identify the issues that cause the jittering.
I have a fixed header div with a 200px height. On scroll, the height is reduced until it reaches a certain height (40px). This gives the effect of the header turning into a fixed header once user reaches the content container.
This works smooth in Firefox and Chrome, however, Safari is glitchy. Particularly, when user scrolls back up and the header increases in height. See JS Fiddle here.
$(window).scroll(function () {
var $scrollTop = $(window).scrollTop(),
$element = $('.content-container').offset().top,
$distance = ($element - $scrollTop);
if ($scrollTop < $element - $newHeight) {
header.height($distance);
}
});
What is causing safari to glitch so much on the height increase? What can I do to smooth this out?
The way to smoothen out this effect in Safari is to change the approach all together. Instead of changing the height of the header on scroll, make the content container position:relative; and set a higher z-index. Then when scroll reaches the bottom of your header (or wherever point you'd like to make the header sticky), change the z-index of the header to be higher than the content container and set it's height to your desired size.
Here is the JS. Please see this JS Fiddle for demo and the rest of code (css, html).
$(window).scroll(function () {
if ($scrollTop > $element - $newHeight) {
header.height($newHeight).css("z-index", 1000);
}
else {
header.css("z-index", 100).height($oldHeight);
}
});
Also, consider using requestAnimationFrame instead of onScroll. It''ll be lighter weight.
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.
I've got a solution for keeping a sidebar in the viewport as you scroll up and down the page. Problem comes in when the sidebar is longer than the content area, and you keep scrolling you get this jittering effect as the sidebar keeps pushing the footer down.
I've got an example of this setup in jsFiddle: http://jsfiddle.net/U9F7w/2/ (full screen: http://jsfiddle.net/U9F7w/2/embedded/result/ )
My question is, is there a way to make the sidebar stop once it touches the bottom/footer area?
I've read some solutions about setting the sidebar to absolute, unfortunately it's an existing site and changing the position didn't work and messed with a lot of the existing page elements.
Here's the jQuery/js I'm working with:
// set the offset
var sidebarOffset = $(".sidebar").offset();
var sidebarPadding = 15;
// when the window scrolls, keep sidebar in view
$(window).scroll(function() {
if ($(window).scrollTop() > sidebarOffset.top) {
$(".sidebar").stop().animate({marginTop: $(window).scrollTop() - sidebarOffset.top + sidebarPadding });
}
else {
$(".sidebar").stop().animate({marginTop: 0});
};
});
edit
One thing I thought about was (not sure if this is possible) to detect if the bottom of one div was lower than the bottom of another, stop the scrolling. Is there a way to detect if the bottom of one div is lower than the other?
Check if the sidebar's height is greater then that of the content:
var ct = $(".content");
var sb = $(".sidebar");
var sbOffsetTop = sb.offset().top;
var sbPadding = 15;
$(window).scroll(function() {
if (sb.height() < ct.height()) {
if ($(window).scrollTop() > sbOffsetTop) {
sb.stop().animate({top: $(window).scrollTop() - sbOffsetTop + sbPadding });
}
else {
sb.stop().animate({top: 0});
};
};
});
See demo fiddle with large content and demo fiddle with large sidebar.
And I don't know why exactly, I would use top in conjunction with position: relative, but marginTop works also fine.