Scroll Bouncing With Fixed Positioning - javascript

I have a fixed element that attaches on my page when you reach it in the scroll. This element can sometimes have content above it but not below it, meaning the page depth might not be deep enough to support this kind of behavior, because of this it prevents the user from reaching the bottom of the page and causes the page to bounce, presumably because it's removing the element from the scroll when it fixes, which causes the condition in the scroll event function to no longer be true. The gif shows the undesired effect when this happens.
Demonstrated here in this fiddle: https://jsfiddle.net/dcsjx625/8/
The pages are dynamic so removing the effect for a single page is not possible.
<body>
<div class="header">
<img src="http://placehold.it/600x401">
</div>
<div class="content-parent">
<div class="content">
<img src="http://placehold.it/600x400">
<img src="http://placehold.it/600x400">
<img src="http://placehold.it/600x400">
</div>
</div>
<div class="footer-content">
Footer
</div>
</body>
jQuery:
var $stickyChainOffset = $('.content').offset();
var $stickyChain = $('.content');
var $fixedWidth = $('.content').parent().width();
function checkScroll() {
if ($(window).scrollTop() > $stickyChainOffset.top - 100) {
$stickyChain.css('position', 'fixed').css('top', '100px').css('max-width', $fixedWidth);
} else {
$stickyChain.css('position', 'static').css('max-width', 'initial');
}
};
$(window).scroll(function() {
checkScroll();
});
/* Updates the $fixedWidth variable on resize */
$(window).resize(function() {
$fixedWidth = $('.content').parent().width();
$(window).scroll();
});
Ideally I want to prevent the sticking effect if the element is close enough to the bottom of the page that it might cause a problem.
I've tried calculating the page depth vs the element height in the checkScroll() function like so but even this isn't working. I feel like I'm right on the edge of solving this.:
function checkScroll() {
height = $stickyChain.height() + 100;
depth = $(document).height() - $stickyChainOffset.top;
if ($(window).scrollTop() > $stickyChainOffset.top - 100 && depth > height) {
$stickyChain.css('position', 'fixed').css('top', '100px').css('max-width', $fixedWidth);
} else {
$stickyChain.css('position', 'static').css('max-width', 'initial');
}
};
Any help would be greatly appreciated!

I gotta be honest, I understand your problem but I have no idea when and why you'd run into this exact behavior. That said, here's my workaround and some notes:
The height of your content needs to be 2x the height of fixed element in order to maintain the scrollbar. Otherwise, once the element is fixed, your document entirely loses the scrollbar.
I'm saving the original offset of fixed element to a variable that is used as a marker for future reset. However, I am also redefining the $stickyChainOffset in every scroll event, that you used to define only once. I'm doing this because it changes once fixed.
You can comment and uncomment the padding I saved in css to see how the page behaves in various cases.
If you have any other questions, let me know.
https://jsfiddle.net/1fke1j3d/1/

Related

Adding offset().top amount on scroll

I have a fixed piece of text and I'm trying to add a different class each time the text enters a div on scroll. I've got it working no problem. But if I add an offset amount to the fixed text e.g.
top: 400px
I need to counter this offset in the JS. But I can't seem to figure it out. I've tried using:
.offset().top 400);
But it's not working. Here's a code i'm currently using:
HTML
<p class="text">TEXT HERE</p>
<div class="section1"></div>
<div class="section2"></div>
<div class="section3"></div>
<div class="section4"></div>
JS
$(window).scroll(function (event) {
var scroll = $(window).scrollTop();
$('.text').toggleClass('blue',
scroll >= $('.section1').offset().top
);
$('.text').toggleClass('magenta',
scroll >= $('.section2').offset().top
);
$('.text').toggleClass('green',
scroll >= $('.section3').offset().top
);
$('.text').toggleClass('orange',
scroll >= $('.section4').offset().top
);
});
//trigger the scroll
$(window).scroll();//ensure if you're in current position when page is refreshed
The text needs to add class as soon as it enters the relevant div.
Here's a working fiddle: http://jsfiddle.net/6PrQW/334/
So you did most everything right, but I think where you went wrong is here: var scroll = $(window).scrollTop();
You don't want to calculate using the window offset, rather you want to use the offset of your sticky text. So instead use: var scroll = $('.text').offset().top;
Let me know if that helps.
edit,
and here is your fiddle with the edits.
Note that I edited your line for setting the blue class since you don't want to match the sticky offset against itself.
To find out when something is within your window, you've gotta use something like...
if($(elem).offset().top - $(window).scrollTop < $(window).height()){
//stuff
}
That should trigger as soon as elem is visible on the page! You can check it against $(window).height()/2, for example, if you want it to trigger in the center of the page instead. Hope this helps!

how to get the height of an element and then apply that value through css

I am trying to get the height of my navigation and apply it to a margin-top so I can offset my banner. My navigation is fixed so I'm trying to compensate for that so my banner isn't hidden underneath.
// Offset Banner to Height of Navigation
function bannerOffset() {
var bannerTop = $('.x-navbar').height();
$('#banner-carousel').css('margin-top', bannerTop);
}
This, I thought would do it, but it doesn't reflect anything at all in the front-end.
UPDATE
$(document).ready(function() {
var bannerTop = $('.x-navbar').outerHeight();
$('.x-main').css('margin-top', bannerTop);
$(window).scroll(function() {
var bannerTopScroll = $('.x-navbar.scroll').outerHeight();
if ($(document).scrollTop() > 1) {
$('.x-main').css('margin-top', bannerTopScroll);
}
});
});
So I thought I had this, but on load, the margin-top of .x-main is 245px. When I scroll it becomes 85px. I can see the numbers go down to that. The issue is when I scroll back up to the top the value doesn't go back to 245px. It's not very consistent, but I often get 144px. I should add that, and this is probably why, I have another function that changes the height of my .x-navbar when .scroll is added to it.
$(window).scroll(function() {
if ($(document).scrollTop() > 1) {
$('.x-navbar').addClass('scroll');
} else {
$('.x-navbar').removeClass('scroll');
}
});
So I am not sure how to make this all smooth and working properly. If I reload the page the .x-mainis back to 245px. When I scroll back to the top it's not calculating properly.
Your code works. Maybe you want to use $.outerHeight() instead, your selectors are wrong, or you're experiencing margin collapsing.
It's worth noting that $.height() returns an integer value, so in your $.css() line, you should change bannerTop to bannerTop + 'px' so that the CSS has a unit and not just a number... but it looks like jQuery is doing that automagically for me here. You might try it and see.
var bannerTop = $('.x-navbar').height();
$('#banner-carousel').css('margin-top', bannerTop);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="x-navbar">x-navbar<br>x-navbar<br>x-navbar<br>x-navbar<br>x-navbar<br>x-navbar<br>x-navbar<br>x-navbar<br></div>
<div id="banner-carousel">banner carousel</div>

How to stick a div at the bottom of the page while scrolling after checkbox is checked

Usually I don't ask questions...I'm looking for a solution until I give up,
and this is the case here.
There are many similar questions to my but after a thorough search I found nothing.
So the question is:
After selecting a checkbox the div at the bottom of the page
shuold be sticky untill the user scrolling down to the original place where it was.
I have a great example from kickstarter web site :
If only I could know how they do it :)
If I was not clear enough I'd love to explain myself better.
Thanks in advance
After clicking on checkbox,
You can add these CSS lines to div
position:fixed;
bottom:0;
you want to add position: fixed and attach it to the bottom of the container when checked
html
<div class="wrapper">
<input type="checkbox" id="check"/>
<div id="foot"></div>
</div>
js
var check = document.getElementById('check');
var foot = document.getElementById('foot');
check.addEventListener('change', function () {
if (check.checked) {
foot.style.position = 'fixed';
foot.style.bottom = 0;
}
});
fiddle - http://jsfiddle.net/qak2ept6/
EDIT - http://jsfiddle.net/qak2ept6/1/ restore when unchecked
EDIT EDIT - http://jsfiddle.net/qak2ept6/3/ attach on scroll
when you check the check box. create div with position fixed and store the offset of the bottom edge of the window that would be normally your window height. Assign scroll event and keep checking if the scroll value is equal to the offset you have stored and when it reached just remove the fixed position from the div.
My guess (and if I was doing it) It'll be done by monitoring scroll position and applying a css style or not accordingly.
Something like
Inject it in invisible state in to the document
Note it's position (y coord)
Apply class to make it stick to the bottom of the window and show
On scroll, as soon as you get near the expected yCoord, remove the class and let it assume it's rightful place in the document
On further scroll (when you scroll away), re-apply class until you scroll back
HTH
If i have understood your question, I guess what you want is here
function sticky_relocate() {
var window_top = $(window).scrollTop();
var div_top = $('#sticky-anchor').offset().top
if (window_top > div_top) {
$('#sticky').addClass('stick');
} else {
$('#sticky').removeClass('stick');
}
}
$(function () {
$(window).scroll(sticky_relocate);
sticky_relocate();
});
If not, please explain us with more code and what exactly you need

how to stick the footer to the bottom of the page while moving it upward in a parallax-like effect?

I have a project where the requirement is to move the footer ( #footer ) upward while scrolling down the page in a parallax-like effect. When you start scrolling down the page, the footer should start moving upward only until it's visible in the (bottom part of the) viewport.
The footer should have covered most of the preceding <div> half way up and in full when it has reached the top of the viewport.
The page may have a similar html structure like this :
<body>
<div id="sectionA" class="div">First section</div>
<div id="sectionB" class="div">Second section</div>
<div id="sectionC" class="div">Third section
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div id="footer" class="div cf">Footer</div>
</body>
The parallax-like effect is achieved via javascript/jQuery adding a dynamic negative value to the top CSS property of the (relative positioned) footer. Here is the code for what it matters :
var $window = jQuery(window),
$footer = jQuery("#footer"),
$viewport = window.innerHeight,
$startEffect = $footer.offset().top - $viewport;
function footerParallax() {
var $scrollPos = $window.scrollTop() - $startEffect,
$ratio = 0.6;
$footer.css({
top: -($scrollPos * $ratio)
});
}
$window.scroll(function () {
footerParallax();
});
The (obvious) issue is that as soon as the top property starts getting a negative value, the footer starts moving away from the bottom of the page.
I have prepared a JSFIDDLE and assigned colors to each section and body to make it clearer. The body (dark-red) is visible under the footer after scrolling to the bottom.
What have I tried?
modifying the margin-top instead of the top property:  this does the trick, however the preceding <div> that has to be covered by the footer (#sectionC in the example above) overlaps the contents of the footer and breaks its layout regardless that it is not visible due to its z-index property (added some floating boxes in the fiddle to make it evident.... a clearfix hack didn't help either.)
setting a static position to the footer: neither top or margin-top have effect over a static element.
Changing/reducing dynamically the height of #sectionC instead of top of footer to produce the effect of moving the second upwards :  the footer stops moving as soon as height is equal to 0 (neither negative size or negative paddings are allowed)
Changed the height dynamically of the html and/or body tags to no avail.
I have also tried some parallax plugins like skrollr and skrollr-stylesheets and some others.
The problem with this solution (same with others) is that it relays in an specific (offset) position of the footer measured in px and set in a data attribute, but if the content changes dynamically, for example using the masonry plugin to arrange elements in another section of the document, the measures become inaccurate and the footer may start moving too early or too late.
By the way, other CSS sticky-footer techniques won't work because, well, they actually push the footer to the bottom of the page, and here we are doing the opposite.
I guess the question is either :
how to keep the footer stick to the bottom of the page while it is moved upwards? - or -
how to reduce the gap to 0 between the end of the document and the bottom edge of the footer?
I am starting to think that this issue has not a real solution the way it is, or maybe I am already too tired to see the obvious. I am interested in learning alternative solutions or hacks via CSS / javascript / jQuery or all of the above.
Bear in mind that I am not asking how to create the parallax effect UNLESS a totally different approach (or tweaks to the existing js code) solves the position issue.
IMPORTANT : Please consider that this is a WP site with an XHTML 1.0 Transitional DOCTYPE, and has installed many other jQuery plugins like masonry, scrollTo, jQuery UI, etc. I may have not control to change many things from the original structure (and I don't want to) so the idea is to implement this without breaking too many things and from a modular script.
EDIT #1 : Added a graphic to clarify the question.
Figure A. shows a regular web page scrolled down to the end. The red square represents the viewport and the footer (grey) is slighted moved to the right for illustration purposes. The body has a reddish background color (not visible in normal conditions) just for illustration purposes too. NOTE: the height of each section as well as the height of the footer is determined by their content (forms, images, text, etc.) so is NOT fixed.
Figure B. shows the current issue: If footer slides up in a parallax-like effect (see JSFIDDLE for reference) while scrolling down the page, it starts covering any preceding section above it (WITHOUT modifying neither its own height or the height of the preceding sections) AND it also starts separating itself from the bottom of the page, therefore the body's color background becomes visible. NOTE: the bigger the viewport is (fullscreen mode for instance) the higher the footer is moved upward (and more content is covered by it)
Figure C. is the expected result: the footer should be stuck to the bottom of the page, in other words, it should be the last visible element after the page has been totally scrolled down (and not the body background as in Figure B.) Notice that the contents and the size of each section (including the footer) should (ideally) remain untouched. Having said that, adding padding bottom to the footer or increasing its height is not the expected result since it would break its original visual layout.
Updated Version
Below is an updated version that should better matches your requirements.
This version goes back to relative positioning for the footer element and uses margin-top to position it.
margin-top is calculated off of the previous elements offset, height and current window scroll position. It then uses either
the viewport height if the footer starts offscreen
the initial top value of the footer element ($startEffect) if the footer started onscreen
to determine the actual value for margin-top.
To help keep the footer's layout from being affected by this, wrapping the content of the footer in an absolutely positioned div did the trick for the sample code provided.
Example Fiddle
CSS:
#footer > div {
position: absolute;
top: 0;
left: 0;
...
}
HTML:
<div id="footer" class="div cf"><div>Footer</div></div>
Code:
var $window = jQuery(window),
$footer = jQuery("#footer"),
$viewport = window.innerHeight,
$startEffect = $footer.offset().top;
$prev = $footer.prev(),
$useStartEffect = $startEffect < $viewport;
function footerParallax() {
var $scrollPos = $window.scrollTop() - $startEffect,
$ratio = 0.6;
var prevOffset = $prev.offset().top + $prev.height() - $window.scrollTop();
var marginTop = 0;
if(prevOffset < $viewport && prevOffset < $startEffect) {
if($useStartEffect) {
marginTop = (prevOffset - $startEffect)*$ratio;
} else {
marginTop = (prevOffset - $viewport)*$ratio;
}
}
$footer.css({
"margin-top": marginTop + 'px'
});
}
$window.scroll(function () {
footerParallax();
});
footerParallax();
How was it solved?
As I mentioned in my question, I was too tired to see the obvious but #dc5's answer put me on the right track :
To help keep the footer's layout from being affected,
wrapping the content of the footer in an absolutely
positioned div does the trick
Based on that comment, the answer became simpler than the whole code he proposed needing only :
(dynamically) wrapping the content of the footer in an absolutely positioned div using jQuery's .wrapInner() method
animating the footer by setting the margin-top property instead of the top property
So this extra CSS :
#footerInnerWrapper {
position: absolute;
top:0;
left:0;
width: 100%;
background-color: #666 /* same as footer */
}
and the tweaked original code
var $window = jQuery(window),
$footer = jQuery("#footer"),
$viewport = window.innerHeight,
$startEffect = $footer.offset().top - $viewport;
// add inner wrapper
$footer.wrapInner('<div id="footerInnerWrapper" />');
function footerParallax() {
var $scrollPos = $window.scrollTop() - $startEffect,
$ratio = 0.6;
$footer.css({
// top: -($scrollPos * $ratio)
marginTop: -($scrollPos * $ratio)
});
}
$window.scroll(function () {
footerParallax();
});
did the trick. See JSFIDDLE
This does what I think you need, the footer sticks when it has scrolled in view entirely:
jsFiddle
Code added:
function footerParallax() {
var $scrollPos = $window.scrollTop() - $startEffect,
$ratio = 0.6,
$newTop = -($scrollPos * $ratio),
$oldTop = parseInt($footer.css('top')),
$nonRelTop = $footer.offset().top - $oldTop,
$wanted = ($window.scrollTop()+$viewport-$footer.height());
if ($nonRelTop + $newTop < $wanted) {
$('#sectionC').css('display', 'none');
$wanted = ($window.scrollTop()+$viewport-$footer.height());
$nonRelTop = $footer.offset().top - $oldTop;
$newTop = $wanted - $nonRelTop;
} else {
$('#sectionC').css('display', 'block');
}
$footer.css('top', $newTop);
}
$window.scroll(footerParallax);
And in the CSS I added this so that $footer.css('top') wouldn't produce NaN:
#footer {
top:0;
/* ... */
}
EDIT: A completely new approach after more clarification of OP. I now have a fixed position footer that starts increasing in height to take over the entire screen when the user has scrolled passed half of the document. The HTML, CSS and Javascript have all been updated to achieve this:
jsFiddle

Stop page from scrolling if hovering div [duplicate]

This question already has answers here:
Prevent scrolling of parent element when inner element scroll position reaches top/bottom?
(32 answers)
Closed 9 years ago.
I have a div that is scrollable, but whenever you reach the bottom/top of it, it begins to scroll the entire page. That could be annoying for users who scroll fast, and then the entire page starts scrolling unexpectedly.
I need something where if you are hovering over the div, the page is not scrollable.
I have tried this by adding CSS when I hover the div...
body {
overflow:hidden;
}
...It works but there is one problem. The scrollbar disappears and that looks kind of stupid to have it disappearing/reappearing. Any way to achieve the same effect but keep the scrollbar visible? I have seen it done with Facebook chat.
Here is a very simple way to stop the propagation with no plugins, just jQuery.
Update: The code has been updated to work correctly in IE9+. Have not tested in previous versions.
First, create a class on your <div> to mark it as having this behavior. In my example, I use the class .Scrollable.
<div class="Scrollable">
<!-- A bunch of HTML here which will create scrolling -->
</div>
The jQuery to disable is:
$('.Scrollable').on('DOMMouseScroll mousewheel', function(ev) {
var $this = $(this),
scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = $this.height(),
delta = (ev.type == 'DOMMouseScroll' ?
ev.originalEvent.detail * -40 :
ev.originalEvent.wheelDelta),
up = delta > 0;
var prevent = function() {
ev.stopPropagation();
ev.preventDefault();
ev.returnValue = false;
return false;
}
if (!up && -delta > scrollHeight - height - scrollTop) {
// Scrolling down, but this will take us past the bottom.
$this.scrollTop(scrollHeight);
return prevent();
} else if (up && delta > scrollTop) {
// Scrolling up, but this will take us past the top.
$this.scrollTop(0);
return prevent();
}
});
In essence, what this does is to detect which direction the scrolling is being requested in (based on the originalEvent.wheelDelta: positive = up, negative = down). If the requested delta of the mousewheel event would move scrolling past the top or bottom of the <div>, cancel the event.
In IE, especially, scrolling events which go past a child element's scrollable area then roll up to parent elements, and the scrolling continues regardless of the event being canceled. Because we cancel the event in any case, and then control the scrolling on the child through jQuery, this is prevented.
This is loosely based on the way that this question solves the problem, but does not require the plugin, and is cross-browser compliant with IE9+.
Here is a working jsFiddle demonstrating the code in-action.
Here is a working jsFiddle demonstrating the code in-action, and updated to work with IE.
Here is a working jsFiddle demonstrating the code in-action, and updated to work with IE and FireFox. See this post for more details about the necessity of the changes.
maybe have a look to
How to disable scrolling temporarily?
This is a sample to stop and activate scroll

Categories