[javascript] Stop fixed position at footer

I'm looking for a solution to the popular issue of stopping a fixed object at the footer of the page.

I basically have a fixed "share" box in the bottom left corner of the screen and I don't want it to scroll over the footer, so I need it to stop about 10px above the footer.

I've looked at other questions here as well as others. The closest/most simple demo I could find is http://jsfiddle.net/bryanjamesross/VtPcm/ but I couldn't get it to work with my situation.

Here's the html for the share box:

    <div id="social-float">
        <div class="sf-twitter">
            ...
        </div>

        <div class="sf-facebook">
            ...
        </div>

        <div class="sf-plusone">
            ...
        </div>
    </div>

...and the CSS:

#social-float{
position: fixed;
bottom: 10px;
left: 10px;
width: 55px;
padding: 10px 5px;
text-align: center;
background-color: #fff;
border: 5px solid #ccd0d5;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
display: none;
}

The footer is #footer and it doesn't have a fixed height, if that makes any difference.

If someone could assist me in creating a simple jQuery solution for this, I'd much appreciate it!

This question is related to javascript jquery css scroll fixed

The answer is


If your elements are glitching this is probably because when you change the position to relative the Y position of the footer increases which tries to send the item back to fixed which creates a loop. You can avoid this by setting two different cases when scrolling up and down. You don't even need to reference the fixed element, just the footer, and window size.

const footer = document.querySelector('footer');
document.addEventListener("scroll", checkScroll);

let prevY = window.scrollY + window.innerHeight;
function checkScroll() {
  let footerTop = getRectTop(footer) + window.scrollY;
  let windowBottomY = window.scrollY + window.innerHeight;
  if (prevY < windowBottomY) {  // Scroll Down
    if (windowBottomY > footerTop)
      setScrolledToFooter(true) // using React state. Change class or change style in JS.
  } else { // Scroll Up
    if (windowBottomY <= footerTop)
      setScrolledToFooter(false)
  }
  prevY = windowBottomY
};

function getRectTop(el) {
  var rect = el.getBoundingClientRect();
  return rect.top;
}

and the position of the element in the style object as follows:

position: scrolledToFooter ? 'relative' : 'fixed'

$(window).scroll(() => {
    const footerToTop = $('.your-footer').position().top;
    const scrollTop = $(document).scrollTop() + $(window).height();
    const difference = scrollTop - footerToTop;
    const bottomValue = scrollTop > footerToTop ? difference : 0;
    $('.your-fixed-element').css('bottom', bottomValue);
});

I went with a modification of @user1097431 's answer:

function menuPosition(){
// distance from top of footer to top of document
var footertotop = ($('.footer').position().top);
// distance user has scrolled from top, adjusted to take in height of bar (42 pixels inc. padding)
var scrolltop = $(document).scrollTop() + window.innerHeight;
// difference between the two
var difference = scrolltop-footertotop;

// if user has scrolled further than footer,
// pull sidebar up using a negative margin
if (scrolltop > footertotop) {
    $('#categories-wrapper').css({
       'bottom' : difference
   });
}else{
    $('#categories-wrapper').css({
       'bottom' : 0
   });
 };
};

I ran into this same issue recently, posted the my solution also here: Preventing element from displaying on top of footer when using position:fixed

You can achieve a solution leveraging the position property of the element with jQuery, switching between the default value (static for divs), fixed and absolute. You will also need a container element for your fixed element. Finally, in order to prevent the fixed element to go over the footer, this container element can't be the parent of the footer.

The javascript part involves calculating the distance in pixels between your fixed element and the top of the document, and comparing it with the current vertical position of the scrollbar relatively to the window object (i.e. the number of pixels above that are hidden from the visible area of the page) every time the user scrolls the page. When, on scrolling down, the fixed element is about to disappear above, we change its position to fixed and stick on top of the page.

This causes the fixed element to go over the footer when we scroll to the bottom, especially if the browser window is small. Therefore, we will calculate the distance in pixels of the footer from the top of the document and compare it with the height of the fixed element plus the vertical position of the scrollbar: when the fixed element is about to go over the footer, we will change its position to absolute and stick at the bottom, just over the footer.

Here's a generic example.

The HTML structure:

<div id="content">
    <div id="leftcolumn">
        <div class="fixed-element">
            This is fixed 
        </div>
    </div>
    <div id="rightcolumn">Main content here</div>
    <div id="footer"> The footer </div>
</div>  

The CSS:

#leftcolumn {
    position: relative;
}
.fixed-element {
    width: 180px;
}
.fixed-element.fixed {
    position: fixed;
    top: 20px;
}
.fixed-element.bottom {
    position: absolute;
    bottom: 356px; /* Height of the footer element, plus some extra pixels if needed */
}

The JS:

// Position of fixed element from top of the document
var fixedElementOffset = $('.fixed-element').offset().top;
// Position of footer element from top of the document.
// You can add extra distance from the bottom if needed,
// must match with the bottom property in CSS
var footerOffset = $('#footer').offset().top - 36;

var fixedElementHeight = $('.fixed-element').height(); 

// Check every time the user scrolls
$(window).scroll(function (event) {

    // Y position of the vertical scrollbar
    var y = $(this).scrollTop();

    if ( y >= fixedElementOffset && ( y + fixedElementHeight ) < footerOffset ) {
        $('.fixed-element').addClass('fixed');
        $('.fixed-element').removeClass('bottom');          
    }
    else if ( y >= fixedElementOffset && ( y + fixedElementHeight ) >= footerOffset ) {
        $('.fixed-element').removeClass('fixed');           
        $('.fixed-element').addClass('bottom');
    }
    else {
        $('.fixed-element').removeClass('fixed bottom');
    }

 });

I've made some changes to the second most popular answer as i found this worked better for me. The changes use window.innerHeight as it is more dynamic than adding your own height for the nav (above used + 570). this allows the code to work on mobile, tablet and desktop dynamicly.

$(window).scroll(() => {
            //Distance from top fo document to top of footer
            topOfFooter = $('#footer').position().top;
             // Distance user has scrolled from top + windows inner height
             scrollDistanceFromTopOfDoc = $(document).scrollTop() + window.innerHeight;
             // Difference between the two.
             scrollDistanceFromTopOfFooter = scrollDistanceFromTopOfDoc - topOfFooter; 
            // If user has scrolled further than footer,
              if (scrollDistanceFromTopOfDoc > topOfFooter) {
                // add margin-bottom so button stays above footer.
                $('#floating-button').css('margin-bottom',  0 + scrollDistanceFromTopOfFooter);
              } else  {
                // remove margin-bottom so button goes back to the bottom of the page
                $('#floating-button').css('margin-bottom', 0);
              }
            });

This worked for me -

HTML -

<div id="sideNote" class="col-sm-3" style="float:right;">

</div> 
<div class="footer-wrap">
        <div id="footer-div">
        </div>      
</div>

CSS -

#sideNote{right:0; margin-top:10px; position:fixed; bottom:0; margin-bottom:5px;}

#footer-div{margin:0 auto; text-align:center; min-height:300px; margin-top:100px; padding:100px 50px;}

JQuery -

function isVisible(elment) {
    var vpH = $(window).height(), // Viewport Height
        st = $(window).scrollTop(), // Scroll Top
        y = $(elment).offset().top;

    return y <= (vpH + st);
}

function setSideNotePos(){
    $(window).scroll(function() {
        if (isVisible($('.footer-wrap'))) {
            $('#sideNote').css('position','absolute');
            $('#sideNote').css('top',$('.footer-wrap').offset().top - $('#sideNote').outerHeight() - 100);
        } else {
            $('#sideNote').css('position','fixed');
            $('#sideNote').css('top','auto');
        }
    });
}

Now call this function like this -

$(document).ready(function() {
    setSideNotePos();
});

PS - The Jquery functions are copied from an answer to another similar question on stackoverflow, but it wasn't working for me fully. So I modified it to these functions, as they are shown here. I think the position etc attributes to your divs will depend on how the divs are structured, who their parents and siblings are.

The above function works when both sideNote and footer-wraps are direct siblings.


I've just solved this problem on a site I'm working on, and thought I would share it in the hope it helps someone.

My solution takes the distance from the footer to the top of the page - if the user has scrolled further than this, it pulls the sidebar back up with a negative margin.

$(window).scroll(() => { 
  // Distance from top of document to top of footer.
  topOfFooter = $('#footer').position().top;
  // Distance user has scrolled from top, adjusted to take in height of sidebar (570 pixels inc. padding).
  scrollDistanceFromTopOfDoc = $(document).scrollTop() + 570;
  // Difference between the two.
  scrollDistanceFromTopOfFooter = scrollDistanceFromTopOfDoc - topOfFooter;

  // If user has scrolled further than footer,
  // pull sidebar up using a negative margin.
  if (scrollDistanceFromTopOfDoc > topOfFooter) {
    $('#cart').css('margin-top',  0 - scrollDistanceFromTopOfFooter);
  } else  {
    $('#cart').css('margin-top', 0);
  }
});

A pure css solution

<div id="outer-container">
    <div id="scrollable">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in vulputate turpis. Curabitur a consectetur libero. Nulla ac velit nibh, ac lacinia nulla. In sed urna sit amet mauris vulputate viverra et et eros. Pellentesque laoreet est et neque euismod a bibendum velit laoreet. Nam gravida lectus nec purus porttitor porta. Vivamus tempor tempus auctor. Nam quis porttitor ligula. Vestibulum rutrum fermentum ligula eget luctus. Sed convallis iaculis lorem non adipiscing. Sed in egestas lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc dictum, lacus quis venenatis ultricies, turpis lorem bibendum dui, quis bibendum lacus ante commodo urna. Fusce ut sem mi, nec molestie tortor. Mauris eu leo diam. Nullam adipiscing, tortor eleifend pellentesque gravida, erat tellus vulputate orci, quis accumsan orci ipsum sed justo. Proin massa massa, pellentesque non tristique non, tristique vel dui. Vestibulum at metus at neque malesuada porta et vitae lectus.
    </div>
    <button id="social-float">The button</button>
</div>
<div>
Footer
</div>
</div>

And css here

#outer-container {
    position: relative;
}

#scrollable {
    height: 100px;
    overflow-y: auto;
}

#social-float {
    position: absolute;
    bottom: 0px;
}

Here is the @Sang solution but without Jquery.

_x000D_
_x000D_
var socialFloat = document.querySelector('#social-float');_x000D_
var footer = document.querySelector('#footer');_x000D_
_x000D_
function checkOffset() {_x000D_
  function getRectTop(el){_x000D_
    var rect = el.getBoundingClientRect();_x000D_
    return rect.top;_x000D_
  }_x000D_
  _x000D_
  if((getRectTop(socialFloat) + document.body.scrollTop) + socialFloat.offsetHeight >= (getRectTop(footer) + document.body.scrollTop) - 10)_x000D_
    socialFloat.style.position = 'absolute';_x000D_
  if(document.body.scrollTop + window.innerHeight < (getRectTop(footer) + document.body.scrollTop))_x000D_
    socialFloat.style.position = 'fixed'; // restore when you scroll up_x000D_
  _x000D_
  socialFloat.innerHTML = document.body.scrollTop + window.innerHeight;_x000D_
}_x000D_
_x000D_
document.addEventListener("scroll", function(){_x000D_
  checkOffset();_x000D_
});
_x000D_
div.social-float-parent { width: 100%; height: 1000px; background: #f8f8f8; position: relative; }_x000D_
div#social-float { width: 200px; position: fixed; bottom: 10px; background: #777; }_x000D_
div#footer { width: 100%; height: 200px; background: #eee; }
_x000D_
<div class="social-float-parent">_x000D_
    <div id="social-float">_x000D_
        float..._x000D_
    </div>_x000D_
</div>_x000D_
<div id="footer">_x000D_
</div>
_x000D_
_x000D_
_x000D_


I was working on stopping the adbanner at a certain point before footer. And played with the code I found above.

Worked for me (banner disappears right before footer and reappears on scroll to top):

<style>
#leftsidebanner {width:300px;height:600px;position: fixed; padding: 0;top:288px;right:5%;display: block;background-color: aqua}
</style>

<div id="leftsidebanner">
</div>


<script>

    $.fn.followTo = function (pos) {
    var stickyAd = $(this),
    theWindow = $(window);
    $(window).scroll(function (e) {
      if ($(window).scrollTop() > pos) {
        stickyAd.css({'position': 'absolute','bottom': pos});
      } else {
        stickyAd.css({'position': 'fixed','top': '100'});
      }
    });
  };
  $('#leftsidebanner').followTo(2268);


</script>

You can now use

#myObject{
  position:sticky;
}

Hope this helps..


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width

Examples related to scroll

How to window.scrollTo() with a smooth effect Angular 2 Scroll to bottom (Chat style) Scroll to the top of the page after render in react.js Get div's offsetTop positions in React RecyclerView - How to smooth scroll to top of item on a certain position? Detecting scroll direction How to disable RecyclerView scrolling? How can I scroll a div to be visible in ReactJS? Make a nav bar stick Disable Scrolling on Body

Examples related to fixed

How to fix a header on scroll Fixed header table with horizontal scrollbar and vertical scrollbar on How to make a div have a fixed size? parent & child with position fixed, parent overflow:hidden bug Stop fixed position at footer Fixed positioned div within a relative parent div Fixed position but relative to container HTML table with horizontal scrolling (first column fixed) How can I fix the form size in a C# Windows Forms application and not to let user change its size?