2

I'm using the Bootstrap 4 class "sticky-top" (for position: sticky) to stick elements, you guessed it, at the top.

Is there any way to add an extra class like "is-sticky-top" to the DIV when it's "stuck"?

I found this but I couldn't adapt it to my needs: https://developers.google.com/web/updates/2017/09/sticky-headers

EDIT - To clarify what I need:

The class "sticky-top" adds position:sticky to an element. In my case, I want to add an extra class to a DIV if the position of the DIV is sticky on top. For example to add a shadow to the element but only if it's "stuck" on top. So I need class B only in this case

Cray
  • 5,307
  • 11
  • 70
  • 166
  • 3
    What were you getting, ah, stuck on? – Anthony Mills Nov 22 '17 at 14:57
  • Let me see if I got this straight: you want to add a class `B` to an element when class `A` is present? That cannot serve any purpose, not even in theory. Because at any point in time, you can check if `A` is on the element and you don't need `B` at all. Or maybe I didn't understand your request. If so, please clarify. – tao Nov 22 '17 at 15:14
  • The class "sticky-top" adds position:sticky to an element. In my case, I want to add an extra class to a DIV if the position of the DIV is sticky on top. For example to add a shadow to the element but only if it's "stuck" on top. So I need class B only in this case – Cray Nov 22 '17 at 15:25
  • What do you mean by ***if the position of the DIV is sticky on top***? How are you checking this? What's the criteria? – tao Nov 22 '17 at 15:27
  • That's the problem. I can't check it... With sticky on top i mean something like this: http://filamentgroup.github.io/fixed-sticky/demos/demo.html But I'm using only CSS without JS – Cray Nov 22 '17 at 15:28
  • Are you saying you don't know how to define your request in technical terms? Use non-technical terms. What exactly do you call ***position is sticky on top***? How do you measure the stickyness? Do you want to know if the window scrolled past the point where the element is no longer displayed in its initial place in the flow? – tao Nov 22 '17 at 15:33
  • Both the article you are referencing and the Fixed Sticky plugin you mention are using JS. I don't think there are any way to detect whether an element is actually stuck or not using only CSS. – agrm Nov 22 '17 at 15:35
  • Let's say I've an element on my page. If I scroll down to it and I pass it, it stays in position (like in the link above). Now, when it stays in the position I want to add an extra class for a shadow. I need this class only if the element stays in the position on top of the site. It would be ok to use JS for that. At the moment I'm only using CSS for the sticky element – Cray Nov 22 '17 at 15:37
  • Ok, I now understand what you want to know. The simple answer is: *"You can't do it without JavaScript."* You need to bind a throttled function on window `scroll` for checking your element's position in relation to its parent. This function also needs to be bound to window `load` and `resize` events. – tao Nov 22 '17 at 15:41
  • 1
    Possible duplicate of [Targeting position:sticky elements that are currently in a 'stuck' state](https://stackoverflow.com/questions/25308823/targeting-positionsticky-elements-that-are-currently-in-a-stuck-state) – tao Nov 22 '17 at 15:55

3 Answers3

4

Hey I posted a Medium article about this. May or may not be applicable to your specific use case but I would suggest spending a few min to check it out.

https://medium.com/@parkjoon94/activatable-drop-shadow-on-sticky-elements-d0c12f1ebfdf

So the basic gist of the technique is to use an element to cover your shadow and when the user scrolls, the cover scrolls up to "uncover" the shadow behind it.

Here's a working JSFiddle of it in action: https://jsfiddle.net/parkjoon/tp1e9yad/

.shadow {
  position: sticky;
  top: 200px;
  width: 297px;
  box-shadow: 0px 0.5px 0.5px 1.5px rgba(0,0,0,0.75);
}

.cover {
  position: absolute;
  background: white;
  width: 100%;
  height: 3px;
}
jay p
  • 131
  • 1
  • 6
  • Interesting. A similar technique can also be accomplished using a pseudo-element, adding to it a fixed background image with the same color of the page background. This will reveal the shadow on scrolling, while keeping the background image invisible. The advantage is that is not needed to change the markup. – Marcio Duarte Dec 28 '21 at 21:29
3

I've come up with a fairly good solution that can work with any top/bottom offset. Just give it which elements to look for and it can determine if the stickiness threshold has been met or not.

Scroll/resize events can be a bit too frequent, so if that's an issue for you just use the commented out version below with a debounce to check less often.

Note that this assumes your top/bottom CSS values will be in pixels, other units might not work here.

$(() => {
  const stuckClass = 'is-stuck';
  const $stickyTopElements = $('.sticky-top');
  const $stickyBottomElements = $('.sticky-bottom');

  const determineSticky = () => {
    $stickyTopElements.each((i, el) => {
      const $el = $(el);
      const stickPoint = parseInt($el.css('top'), 10);
      const currTop = el.getBoundingClientRect().top;
      const isStuck = currTop <= stickPoint;
      $el.toggleClass(stuckClass, isStuck);
    });

    $stickyBottomElements.each((i, el) => {
      const $el = $(el);
      const stickPoint = parseInt($el.css('bottom'), 10);
      const currBottom = el.getBoundingClientRect().bottom;
      const isStuck = currBottom + stickPoint >= window.innerHeight;
      $el.toggleClass(stuckClass, isStuck);
    });
  };

  //run immediately
  determineSticky();

  //Run when the browser is resized or scrolled
  //Uncomment below to run less frequently with a debounce
  //let debounce = null;
  $(window).on('resize scroll', () => {
    //clearTimeout(debounce);
    //debounce = setTimeout(determineSticky, 100);

    determineSticky();
  });

});
.sticky-top,
.sticky-bottom {
  position: sticky;
  z-index: 1000;
  padding: 10px;
  border: 1px solid #CCC;
  background-color: #FFF;
  transition: all 300ms;
}

.sticky-top {
  top: 40px;
}

.sticky-bottom {
  bottom: 40px;
}

.is-stuck {
  background: blue;
  color: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>some text</p>
<p>some text</p>
<p>some text</p>
<div class="sticky-top">
  Sticky to the top!
</div>

<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<div class="sticky-bottom">
  Sticky to the bottom!
</div>

<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
Chris Barr
  • 29,851
  • 23
  • 95
  • 135
2

Currently, the only way to check for that is to calculate the div position on scroll.

$('#wrapper').scroll(function() {
 var wrapperTop = $(this).offset().top;
 var headerTop = $('#header').offset().top;
 if (wrapperTop < headerTop)
   $('#header').css('background','grey');
  else
   $('#header').css('background','red');
});
#wrapper {
  width: 200px;
  height: 200px;
  border: 1px solid black;
  overflow-y: scroll;
}

#header {
  height: 50px;
  background: grey;
  top: -1px;
  position: sticky;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper">
  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</div>
  <div id="header">HEADER TEXT</div>
  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
</div>
FcoRodr
  • 1,583
  • 7
  • 15
  • While variants of this is a widely used way to detect whether an element has reached the top edge of the viewport, there is a significant difference between this and the actual `position:sticky`. Say you have several elements you want to stick to the top. Using `position:sticky` only one element will stick to the top at a time. Your method would make them stack up on top of each others. – agrm Nov 22 '17 at 16:15
  • Yes, this was just a really simple demo of my answer, it will have to vary and be adapted to every scenario – FcoRodr Nov 22 '17 at 16:20