AndryName
AndryName

Reputation: 642

Sticky element inside a div with absolute position on scroll

I have a div with position: absolute and overflow: auto. Inside this div I have a div that should act sticky and should be fixed(top: 0, bottom: 0, overflow: auto) when I scroll.

I can fix this div, but I can't return it to original position because I can't attached the scroll event when this div is fixed.

$('.right').scroll(function() {
    if ($('.scroll').offset().top <= 0) {
        $('.scroll').css({
            'position': 'fixed',
            'top': 0,
            'left': '20px',
            'right': '0',
            'overflow': 'auto'
        })
    }
})

Please check my JSFiddle for more info - JSFIDDLE

Thank you.

Upvotes: 14

Views: 14523

Answers (4)

Jako Basson
Jako Basson

Reputation: 1531

You can try the following example:

Firstly, instead of adding the css as inline styles, create a css class that you can add and remove from the .scroll element.

CSS

.fixed-top {
    position:fixed;
    top:0;
    left:20px;
    right:20px;
}

Wrap your .scroll element with another div which will be used in the javascript to keep track of the original height of your .scroll div.

HTML

<div class="scroll-wrapper">
    <div class="scroll"></div>
</div>

Lastly, store the scrollTop value in a variable when the fixed position is applied for the first time. You can then use that value to determine when to remove the fixed styles from the .scroll div. Also set the height of the .scroll-wrapper element equal to the height of your .scroll element to make sure the content is scrollable.

Javascript

 var startScrollTop = 0;
    $('.right').scroll(function () {
        var $scroll = $('.scroll');
        if ($scroll.offset().top <= 0 && $('.right').scrollTop() > startScrollTop) {
            if (startScrollTop === 0) {
                startScrollTop = $('.right').scrollTop();
            }
             $('.scroll-wrapper').css("height", $('.scroll').height() + 300);
            $scroll.addClass("fixed-top");
        } else {
            $scroll.removeClass("fixed-top");
        }
    })

Take a look at this fiddle. http://jsfiddle.net/a924dcge/25/

Hope that helps!

Upvotes: 0

Alex  Sidorenko
Alex Sidorenko

Reputation: 546

It is very strange task you want to accomplish :)

But anyway there is the problem:

When you set you inner div to position: fixed you positioned this div above your div.right and it is prevents scrolling event from fire.

So what you need is to set pointer-events: none to the div.scroll to allow your div.right listen scroll events without any problems.

But when you do that you will face another problem - when you set your div.scroll to position: fixed it will lose its place inside the div.right and div.right jumps to the top of the scroll automatically. To prevent that you need to create clone of the div.scroll and set his height to 0 initially, and to auto when your inner element is fixed.

Note pointer-events: none - disable all mouse events including the text selection.

There is the code:

JS

$(document).ready(function() {
    var cnt = $('.right');
    var scrollEl = $('.scroll');
    var scrollClone = scrollEl.clone().addClass('clone');
    scrollEl.before(scrollClone);
    cnt.scroll(function() {
        var expression = scrollClone.offset().top <= 0;
        scrollEl.toggleClass('stick', expression);
        scrollClone.toggleClass('stick-clone', expression);
    })
})

CSS

.scroll {
    background: yellow;
    pointer-events: none;
    overflow: hidden; /* Remove top offset from h1*/
}
.scroll.stick {
    position: fixed;
    left: 20px;
    right: 0;
    top: 0;
}
.scroll.clone {
    height: 0;
    overflow: hidden;
}
.scroll.clone.stick-clone {
    height: auto;
}

JSFiddle

Upvotes: 1

plushyObject
plushyObject

Reputation: 1131

Set the outside div to

position: relative;

Set the inside div to

position: absolute;
top: 15px;
right: 15px;

This will put the top right corner of the inside div at the designated location within the parent container. When setting position absolute, the image is set relative to the first parent container with position defined to anything other than default, I believe. If there is no DOM element assigned a position, the absolute element will be positioned relative to the viewport.

Upvotes: 1

user4639281
user4639281

Reputation:

Here's how I would do it. This doesn't position it fixed but it has the same appearance. Once scrollTop is equal to or greater than where the top of the fixed content "should be" then we set the top absolutely to that of scrollTop, if you scroll upwards once the scrollTop reaches the point where the fixed content used to be, it will drop it again.

$(document).ready(function() {
  oldOffset = $('.scroll').offset().top;
  $('.right').scroll(function() {
    if ($('.right').scrollTop() > oldOffset) {
      $('.scroll').css({
        'position': 'absolute',
        'top': $('.right').scrollTop(),
        'left': '20px',
        'right': '0',
        'overflow': 'auto'
      });
    }
  });
});

(Demo)

Upvotes: 8

Related Questions