106

I have an element that is position:fixed and so scrolls with the page how i want it to however. when the user scrolls up I want the element to stop scrolling at a certain point, say when it is 250px from the top of the page, is this possible? Any help or advice would be helpful thanks!

I had a feeling that I would need to use jquery to do so. I tried getting the scrolling or location of the where the user is but got really confused, is there any jquery solutions?

Louise McComiskey
  • 1,855
  • 4
  • 19
  • 20
  • 1
    As far as I know, only a javascript-based solution will do what you want. There is no pure CSS solution that does that. – cdeszaq May 05 '11 at 19:07

13 Answers13

144

Do you mean sort of like this?

http://jsfiddle.net/b43hj/

$(window).scroll(function(){
    $("#theFixed").css("top", Math.max(0, 250 - $(this).scrollTop()));
});

$(window).scroll(function(){
    $("#theFixed").css("top", Math.max(0, 100 - $(this).scrollTop()));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="theFixed" style="position:fixed;top:100px;background-color:red">SOMETHING</div>

<!-- random filler to allow for scrolling -->
STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>STUFF <BR>
James Montagne
  • 77,516
  • 14
  • 110
  • 130
  • Great, thank you so much! Though my task was to create "To the top" button that would be always above footer, I modified this code a bit. See my version here (js coffee): http://jsfiddle.net/9vnwx3fa/2/ – Al17 Aug 19 '15 at 09:26
  • @James Montagne - What would be the solution for reversing this code if I wanted the fixed element sticky bottom: 0; on scroll and then stop 250px before it gets to the bottom of the page? – BoneStarr Feb 03 '16 at 14:48
  • 1
    @BoneStarr that's a bit more complex. You would need to compare the current scrollTop with the height of the page and the viewport. Then you would simply use the same code above, except with `bottom` and this calculated value (offset by 250) in the max. – James Montagne Feb 04 '16 at 03:04
  • @JamesMontagne - Any chance you could elaborate on this fiddle? Would really appreciate it as I am stuck with this one. http://jsfiddle.net/Hf5wH/137/ – BoneStarr Feb 05 '16 at 09:42
  • 1
    @bonestarr Considerably more complicated than this one. You should expand it to multiple lines of code or it's really hard to understand. http://jsfiddle.net/Hf5wH/139/ – James Montagne Feb 06 '16 at 04:34
128

Here's a quick jQuery plugin I just wrote that can do what you require:

$.fn.followTo = function (pos) {
    var $this = this,
        $window = $(window);

    $window.scroll(function (e) {
        if ($window.scrollTop() > pos) {
            $this.css({
                position: 'absolute',
                top: pos
            });
        } else {
            $this.css({
                position: 'fixed',
                top: 0
            });
        }
    });
};

$('#yourDiv').followTo(250);

See working example →

GMA
  • 5,816
  • 6
  • 51
  • 80
mVChr
  • 49,587
  • 11
  • 107
  • 104
20

Here is a complete jquery plugin that solves this problem:

https://github.com/bigspotteddog/ScrollToFixed

The description of this plugin is as follows:

This plugin is used to fix elements to the top of the page, if the element would have scrolled out of view, vertically; however, it does allow the element to continue to move left or right with the horizontal scroll.

Given an option marginTop, the element will stop moving vertically upward once the vertical scroll has reached the target position; but, the element will still move horizontally as the page is scrolled left or right. Once the page has been scrolled back down past the target position, the element will be restored to its original position on the page.

This plugin has been tested in Firefox 3/4, Google Chrome 10/11, Safari 5, and Internet Explorer 8/9.

Usage for your particular case:

<script src="scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="scripts/jquery-scrolltofixed-min.js" type="text/javascript"></script>

$(document).ready(function() {
    $('#mydiv').scrollToFixed({ marginTop: 250 });
});
bigspotteddog
  • 1,449
  • 13
  • 14
  • 1
    Thankyou yes this looks really useful, I used a previous answer as this project was a couple of months ago however I will definately keep this in mind for future projects, it looks easier, Thanks :) – Louise McComiskey Aug 30 '11 at 10:34
10

A possible CSS ONLY solution can be achived with position: sticky;

The browser support is actually really good: https://caniuse.com/#search=position%3A%20sticky

here is an example: https://jsfiddle.net/0vcoa43L/7/

JiiB
  • 1,432
  • 1
  • 12
  • 26
  • 2
    unfortunately not applicable for IE11 – Alejandro Dec 24 '18 at 22:31
  • 2
    This is such a nice and simple solution, it took me a moment to get my head around it, but one of the keys is actually that the parent element needs a height. In my case the child element that should be sticky was floated, so the parent didn't have a height initially. https://stackoverflow.com/a/4805371/368479 put me on the right track with `display: flex;` on the parent. – berliner Dec 02 '20 at 01:38
6

You can do what James Montagne did with his code in his answer, but that will make it flicker in Chrome (tested in V19).

You can fix that if you put "margin-top" instead of "top". Don't really know why it works with margin tho.

$(window).scroll(function(){
    $("#theFixed").css("margin-top",Math.max(-250,0-$(this).scrollTop()));
});

http://jsbin.com/idacel

Jure Ravlić
  • 86
  • 1
  • 4
  • This works great for fixed elements beneath text as well, I have been having a serious issue with small screen displays, and having my fixed scroll elements run up to the browser top on smaller areas like 1024x768. This resolved that issue. – Joshua Feb 23 '17 at 23:10
2

my solution

$(window).scroll(function(){
        if($(this).scrollTop()>425) {
            $("#theRelative").css("margin-top",$(this).scrollTop()-425);
            }   else {
                $("#theRelative").css("margin-top",$(this).scrollTop()-0);
                }
            });
            });
2

In a project, I actually have some heading fixed to the bottom of the screen on page load (it's a drawing app so the heading is at the bottom to give maximum space to the canvas element on wide viewport).

I needed the heading to become 'absolute' when it reaches the footer on scroll, since I don't want the heading over the footer (heading colour is same as footer background colour).

I took the oldest response on here (edited by Gearge Millo) and that code snippet worked for my use-case. With some playing around I got this working. Now the fixed heading sits beautifully above the footer once it reaches the footer.

Just thought I'd share my use-case and how it worked, and say thank you! The app: http://joefalconer.com/web_projects/drawingapp/index.html

    /* CSS */
    @media screen and (min-width: 1100px) {
        #heading {
            height: 80px;
            width: 100%;
            position: absolute;  /* heading is 'absolute' on page load. DOESN'T WORK if I have this on 'fixed' */
            bottom: 0;
        }
    }

    // jQuery
    // Stop the fixed heading from scrolling over the footer
    $.fn.followTo = function (pos) {
      var $this = this,
      $window = $(window);

      $window.scroll(function (e) {
        if ($window.scrollTop() > pos) {
          $this.css( { position: 'absolute', bottom: '-180px' } );
        } else {
          $this.css( { position: 'fixed', bottom: '0' } );
        }
      });
    };
    // This behaviour is only needed for wide view ports
    if ( $('#heading').css("position") === "absolute" ) {
      $('#heading').followTo(180);
    }
1

I wrote a blog post about this that featured this function:

$.fn.myFixture = function (settings) {
  return this.each(function () {

    // default css declaration 
    var elem = $(this).css('position', 'fixed');

    var setPosition = function () {         
      var top = 0;
      // get no of pixels hidden above the the window     
      var scrollTop = $(window).scrollTop();    
      // get elements distance from top of window
      var topBuffer = ((settings.topBoundary || 0) - scrollTop);
      // update position if required
      if (topBuffer >= 0) { top += topBuffer }
      elem.css('top', top);      
    };

    $(window).bind('scroll', setPosition);
    setPosition();
  });
};
0

A Solution using Mootools Framework.

http://mootools.net/docs/more/Fx/Fx.Scroll

  1. Get Position(x & y) of the element where you want to stop the scroll using $('myElement').getPosition().x

    $('myElement').getPosition().y

  2. For a animation sort of scroll use :

    new Fx.Scroll('scrollDivId', {offset: {x: 24,y: 432} }).toTop();

  3. To just set scroll immediately use :

    new Fx.Scroll(myElement).set(x,y);

Hope this Helps !! :D

Zohaib
  • 417
  • 9
  • 24
0

I liked this solution

$(window).scroll(function(){
    $("#theFixed").css("margin-top",Math.max(-250,0-$(this).scrollTop()));
});

my problem was that I had to deal with a position relative container in Adobe Muse.

My solution:

$(window).scroll(function(){
    if($(this).scrollTop()>425) {
         $("#theRelative").css("margin-top",$(this).scrollTop()-425);
    }
});
leu
  • 123
  • 2
  • 14
0

Just improvised mVChr code

$(".sidebar").css('position', 'fixed')

    var windw = this;

    $.fn.followTo = function(pos) {
        var $this = this,
                $window = $(windw);

        $window.scroll(function(e) {
            if ($window.scrollTop() > pos) {
                topPos = pos + $($this).height();
                $this.css({
                    position: 'absolute',
                    top: topPos
                });
            } else {
                $this.css({
                    position: 'fixed',
                    top: 250 //set your value
                });
            }
        });
    };

    var height = $("body").height() - $("#footer").height() ;
    $('.sidebar').followTo(height);
    $('.sidebar').scrollTo($('html').offset().top);
Sreeraj
  • 2,690
  • 6
  • 26
  • 37
  • The reason to store `$(this)` and `$(window)` in variables is so you only have to do `$this.height()` and so on. Instead of setting a top position wouldn't it be better if the plugin stored the original top value and reverted to it when setting a fixed width? Also, what do you mean with `Just improvised mVChr code`? – Cyclonecode Feb 19 '15 at 00:38
0

I adapted @mVchr's answer and inverted it to use for sticky ad positioning: if you need it absolutely positioned (scrolling) until the header junk is off screen but then need it to stay fixied/visible on screen after that:

$.fn.followTo = function (pos) {
    var stickyAd = $(this),
    theWindow = $(window);
    $(window).scroll(function (e) {
      if ($(window).scrollTop() > pos) {
        stickyAd.css({'position': 'fixed','top': '0'});
      } else {
        stickyAd.css({'position': 'absolute','top': pos});
      }
    });
  };
  $('#sticky-ad').followTo(740);

CSS:

#sticky-ad {
    float: left;
    display: block;
    position: absolute;
    top: 740px;
    left: -664px;
    margin-left: 50%;
    z-index: 9999;
}
USER10
  • 939
  • 11
  • 28
cbmtrx
  • 591
  • 8
  • 16
0

I loved @james answer but I was looking for its inverse i.e. stop fixed position right before footer, here is what I came up with

var $fixed_element = $(".some_element")
if($fixed_element.length){
        var $offset = $(".footer").position().top,
            $wh = $(window).innerHeight(),
            $diff = $offset - $wh,
            $scrolled = $(window).scrollTop();
        $fixed_element.css("bottom", Math.max(0, $scrolled-$diff));
    }

So now the fixed element would stop right before footer. and will not overlap with it.

mshahbazm
  • 611
  • 1
  • 11
  • 23