Position Fixed div leaving normal document flow - javascript

I am trying to create a sticky sidebar as follows, the sidebar's height is greater than that of the viewport.
This is the page structure:
<div class="wrapper">
<div class="content__left"> </div>
<div class="sticky"> </div>
<div>
There is a fixed mainmenu at the top of height 54px that scrolls along with the page. I have written this code which works perfectly but the problem is that when the class "fixed-top" and "fixed-bottom" are applied it changes the width of the sidebar and moves it out of the normal document flow. Even when I change the width back to normal it still moves out of the normal flow due to which the result is not smooth. I have tried making changes to margin, translating it but none of it seems to work, I would really appreciate some help here.
Here is the code :
.stop-top{
position:static;
top:0px;
bottom:auto;
}
.fixed-top{
position:fixed;
top:54px;
bottom:auto;
}
.fixed-bottom{
position:fixed;
top:auto;
bottom:0px;
}
.stop-bottom{
position:relative;
bottom:auto;
}
.hang{
position:relative;
bottom:auto;
}
.
// Sidebar div
var stickySidebar = $('.sticky');
var stickyHeight = stickySidebar.height();
var stickyOffsetLeft = $('.sticky').offset().left;
var originalOffset = stickySidebar.offset().top + $('.main-menu').height();
// content__left div
var contentLeft = $('.content__left');
var contentLeftTop = contentLeft.offset().top;
// Floating menu
var mainmenu = $('.main-menu');
var mainmenuHeight = mainmenu.height();
var lastScrollTop = 0;
var onScroll = function () {
var sidebarTop = stickySidebar.offset().top;
var scrollTop = $(window).scrollTop();
var stopPosition;
//triggered on scrolling down
if (scrollTop > lastScrollTop){
// Bottom Position of the viewport
var ViewportBottom = $(window).scrollTop() + $(window).height();
// if class "fixed-top" is found while scrolling down, remove it and add "hang"
if(stickySidebar.hasClass("fixed-top")){
stickySidebar.removeAttr( 'style' );
var newTop = stickySidebar.offset().top;
stickySidebar.removeClass("fixed-top");
stickySidebar.addClass("hang");
// Calculate 'top' for hang class
stickySidebar.css('top', newTop - contentLeftTop);
}
// if bottom of the viewport crosses the bottom of the sidebar,
if(ViewportBottom > $('.sticky').offset().top + $('.sticky').height()){
// Remove any added classes and instead add class "fixed-bottom"
stickySidebar.css("top","");
stickySidebar.removeClass("hang");
stickySidebar.removeClass("stop-top");
stickySidebar.removeClass("stop-bottom");
stickySidebar.addClass("fixed-bottom");
stickySidebar.css({"width":0.3 * stickySidebar.parent().width()});
// stickySidebar.css({"width":0.3 * stickySidebar.parent().width(), "left":stickyOffsetLeft });
}
var contentLeftBottom = $('.content__left').offset().top + $('.content__left').height();
// When bottom of the viewport crosses the bottom of the 'content__left', remove class "fixed-bottom" and add "stop-bottom"
if(ViewportBottom > contentLeftBottom){
stickySidebar.css('width',"");
// stickySidebar.css('left',"");
stickySidebar.removeClass("fixed-bottom");
stickySidebar.addClass("stop-bottom");
stickySidebar.css('top', contentLeft.height() - stickyHeight);
}
}
else {// triggered on scrolling up
// if class "fixed-bottom" is found while scrolling up, remove it and add "hang"
if(stickySidebar.hasClass("fixed-bottom")){
stickySidebar.removeAttr( 'style' );
var newTop = stickySidebar.offset().top;
stickySidebar.removeClass("fixed-bottom");
stickySidebar.addClass("hang");
// Calculate 'top' for hang class
stickySidebar.css('top', newTop - contentLeftTop);
}
// When top of the viewport is less than the top of sidebar, add class "fixed-top"
if(scrollTop + mainmenuHeight < $(".sticky").offset().top){
stickySidebar.css("top","");
stickySidebar.removeClass("hang");
stickySidebar.removeClass("stop-bottom");
stickySidebar.addClass("fixed-top");
stickySidebar.css({"width":0.3 * stickySidebar.parent().width()});
// stickySidebar.css({"width":0.3 * stickySidebar.parent().width(), "left":stickyOffsetLeft});
}
// When top of the viewport is less than top of the 'content__left', add class "stop-top"
if(scrollTop + mainmenuHeight < contentLeftTop){
stickySidebar.removeClass("fixed-top");
stickySidebar.addClass("stop-top");
stickySidebar.removeAttr( 'style' );
}
}
lastScrollTop = scrollTop;
} // onScroll
$(window).on('scroll', onScroll);

Related

scroll smoothly sticky header on wordpress is not working

I am trying to scroll my header slowly and smoothly but it appears quickly.
This my header and this is what I want my header to be.
//Sticky menu after get riched bottom of the viewport
$(window).scroll(function () {
var elementOffset = $(window).scrollTop();
var viewportHeight = $(window).height();
var elementHeight = $('#header').height();
if (viewportHeight < elementOffset) {
$('#header').addClass('header-sticky');
} else if (elementOffset < $('#header').height()) {
$('#header').removeClass('header-sticky');
}
});
Give Transition property to .header-sticky class
.header-sticky{
transition : .5 ease top;
animation:slide-down 5s;
animation-delay:3s;
}

Parallax - Offset element(s), tied to scroll

Banging my head trying to sort out the correct logic for adding simple parallax behavior.
I would like to have a number of elements on a page which start out with their top offset a certain distance (e.g. 300px). Then as you scroll down the page, once the top of the element is revealed it will slowly shift upwards (tied to scroll) until the top of element reaches middle of viewport at which time it's top offset is 0 and it remains in place.
I tried using third party script (Scroll Magic, Stellar, etc), but when I couldn't get it right now I'm trying custom code:
https://jsfiddle.net/louiswalch/5bxz8fku/1/
var $Window = $(window);
var offset_amount = 400;
var window_height = $Window.height();
var window_half = (window_height/2);
var sections = $('SECTION.reveal');
sections.each(function() {
var element = $(this);
// Make sure we always start with the right offset
element.css({top: offset_amount});
$Window.bind('scroll', function() {
var viewport_top = $Window.scrollTop();
var viewport_middle = viewport_top + (window_height/2)
var viewport_bottom = viewport_top + window_height;
var element_top = element.offset().top;
if (element_top > viewport_top && element_top <= viewport_bottom) {
var distance_to_middle = (element_top - viewport_middle);
var amount_to_middle = (distance_to_middle / window_half);
console.log(amount_to_middle);
if (amount_to_middle >= 0) {
element.css({top: (offset_amount * amount_to_middle)+ 'px'});
} else {
// ? Lock to end position ?
}
}
});
});
jsBin demo 1. (margin space effect on both enter and exit)
jsBin demo 2. (preserve 0 margin once touched)
Instead of targeting the section elements, (create and) target their first child elements,
otherwise you'll create a concurrency mess trying to get the top position but simultaneously modifying it.
Also, you cannot rely on fixed 300px margin (i.e: if window height is less than 500px, you're already missing 100px). That space can vary when the screen height is really small, so you also need to find the idealMarg value.
var $win = $(window),
$rev = $('.reveal'),
winH2 = 0,
winSt = 0;
function reveal() {
winSt = $win.scrollTop();
winH2 = $win.height()/2;
$rev.each(function(i, el){
var y = el.getBoundingClientRect().top,
toMiddleMax = Math.max(0, y-winH2),
idealMarg = Math.min(300, toMiddleMax),
margMin = Math.min(idealMarg, idealMarg * (toMiddleMax/winH2));
$(">div", this).css({transform: "translateY("+ margMin +"px)"});
});
}
$win.on({"load resize scroll" : reveal});
*{box-sizing:border-box; -webkit-box-sizing:border-box;}
html, body{height:100%; margin:0;}
section > div{
padding: 40px;
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<div style="background-color:red">1</div>
</section>
<section class="reveal">
<div style="background-color: yellow">2</div>
</section>
<section class="reveal">
<div style="background-color: orange">3</div>
</section>
<section class="reveal">
<div style="background-color: pink">4</div>
</section>
I've used in HTML just a <div> logically, that has to be the one and only first child of a section parent.
You're welcome to tweak the above code to make it more performant.
Hey so here is my go at an awnser.
http://jsbin.com/wibiferili/edit?html,js,output
The jist of it is as follows.
JS
var $Window = $(window),
parallaxFactor = 2;
$('.parallaxblock').each(function(a,b){
var element = $(b);
element.css("top",element.data("pOffset") + "px");
$Window.bind('scroll', function() {
var pos =
// Base Offset
element.data("pOffset")
// parallaxFactor
- ($Window.scrollTop() / parallaxFactor);
pos = pos < 0 ? 0 : pos;
element.animate({"top": pos + "px"},10);
return;
});
});
Styles
body{
height: 4000px;
}
.parallaxblock{
position:fixed;
background:#999;
opacity:.5;
}
Example Usage
<div class="parallaxblock" data-p-offset=100>Im A Block</div>
<div class="parallaxblock" data-p-offset=200>Im Also Block</div>
<div class="parallaxblock" data-p-offset=1500>Im Another Block</div>
So by checking the offest its never lower then 0 we can lock it at the top of the screen once it reaches it.
I get the offset amount of the data tag on the div.
If you wanted to change the rate of scroll in different posistions you could change the parallax factor at a certain percentage of screen height.
Hope this helps.

How to get scroll direction on specific div

My page have a div called #product. I need to fill progress bar when user scroll in #product div. How can I do it using jquery. Thanks.
if (/* page scroll to #product div */){
var scrolled = ??? //percentage of scroll on div
}
You can get current scroll position with this:
currentScroll = $(this).scrollTop() + $(this).innerHeight()
where 100% scroll is:
maxScroll = this.scrollHeight
Then your current progress percentage will be:
(currentScroll / maxScroll) * 100
Use this code:
$('#product').bind('scroll', function() {
var currentScroll = $(this).scrollTop() + $(this).innerHeight(),
maxScroll = this.scrollHeight;
var scrolled = (currentScroll / maxScroll) * 100;
});
See example here.
EDIT:
To let the div come to top on browser scroll add:
$(document).bind('scroll', function() {
$('#product').css({ position: absolute; top: 0; });
});

Sticky navigation with full height divs

I'm using a JQuery plugin called SMINT to create sticky navigation that becomes fixed to the top of the viewport when scrolling. I'm trying to leave a space before and after the navigation at the top of the page and multiple full height divs below.
Using
* {margin: 0; padding: 0; outline: 0;
-moz-box-sizing:border-box;
-webkit-box-sizing:border-box;
box-sizing:border-box;
}
makes the divs full height (minus the sticky nav) but botches the initial navigation. (after scrolling the navigation is fine). Removing the border-box screws up the full-height.
My attempt: https://jsfiddle.net/colintkelly/uxsg6mL8/
Live example: http://www.banditfish.com/black-fives/
You don't need any plugin for that - here is a quick and easy to understand/customize
Approach:
JSnippet demo - using your HTML without smint
var barSelector = ".subMenu",
offSetToTriggerFixed = 1,
offsettofix = $(barSelector).offset().top + offSetToTriggerFixed,
$fixedBar = $(barSelector).eq(0).clone();
//Set cloned style and append to body:
$fixedBar.css({ display:'none', position: 'fixed', top:0, 'z-index':1100});
$('body').append($fixedBar);
//Set heights:
var viewPortHeight = $('body').height(),
navHeight = $(barSelector).outerHeight(),
$anyOtherSec = $('.section').not('.sTop');
$anyOtherSec.css({ height: viewPortHeight - navHeight + 5});
//Trigger when needed:
$(window).scroll(function(){
var fromTop = $(this).scrollTop();
if (fromTop <= offsettofix) $($fixedBar).hide();
else $($fixedBar).show();
});

Parallax on Div center vertically and positioned absolute

In a project, I have a div which take 100% height and 100% width of the window thanks to a script I've wrote (with jQuery, and called on window resize too).
Inside this div, I have an other div which contains a title(H3) and a paragraph(P). This div is centered vertically et horizontally in this 100% parent div.
Now, I want to add a parallax effect on the child div. I mean I want that on scroll, the div moves slower than his parent div. How could I achieve that ? I've tried to wrote it myself but it doesn't work...
My try :
var lastScrollTop = 0;
$(window).scroll(function(event) {
parallax();
});
function parallax() {
var ev = {
scrollTop: document.body.scrollTop || document.documentElement.scrollTop
};
ev.ratioScrolled = ev.scrollTop / (document.body.scrollHeight - document.documentElement.clientHeight);
render(ev);
}
function render(ev) {
var t = ev.scrollTop;
var obj = $('.topBonjour .wrapper');
var top = parseInt(obj.css('top'));
if (t > lastScrollTop) {
newTop = top + 1;
$('.topBonjour .wrapper').css('top', newTop +'px');
} else {
newTop = top - 1;
$('.topBonjour .wrapper').css('top', newTop +'px');
}
}
Thanks a lot,
Cédric

Categories