Search code examples
javascriptjquerytwitter-bootstrapcsssticky

Sticky Bootstrap column should end with parent/content


I have seen various forms of this problem but nothing really helped me to solve the partial sticky sidebar/Bootstrap column behaviour. Let me start with the problem itself.

There is a big image close to the top of my page. Because of the page complexity, I am using Bootstrap column grid. The image spans over, let's say, 10 columns and I have left 2, belonging to the same row, on the left side to store a sidebar. This also allows me to vertically align the sidebar next to the image.

Now, the sidebar, what is now a Bootstrap column, should go sticky and should stay vertically aligned to the viewport once the scrollbar passes by. You can see in the fiddle that it kind of "jumps" instead of transitioning smoothly.

The other problem is that the sticky element/column should only remain sticky as long as its parent/container is visible. Which means that it should transition/be relative to the end of that container. Right now I have only managed to keep it sticky till the end of the page. It should stop above the red line (depicted in the fiddle).

Here is my jQuery logic so far.

$(document).ready(function(){
  $(window).scroll(function(){
    var elem = $("#refScroller").offset().top - ($("#refScroller").height() / 2);
    var windowvalue = $(window).scrollTop();

    if (elem <= windowvalue) {
      $("#wannabeSticky").addClass("sticky");
    }
    else {
      $("#wannabeSticky").removeClass("sticky");
    }
  });
});

I would really appreciate some ideas and hints as this has been bothering me for two days. I would love to keep the Bootstrap grid structure if possible, but feel free to give any suggestions, even those who depict the sidebar as a pure absolute div, as long as the sticky-ness works.

Thanks in advance!

EDIT: I know there is a similar problem already here, but it seems I can't make the JS logic work for my case.


Solution

  • So, having spent another day on it, it seems I reached a decent jQuery version that gets the job done. There is my updated fiddle.

    $(document).ready(function(){
      var passedMobileNavi = false;
    
      function stickySocialNavi(reference, valueExtracted) {
        var refTop = $(reference).offset().top - valueExtracted;
        var scrollTop = $(window).scrollTop();
        return (refTop <= scrollTop);
      }
    
      $(window).scroll(function() {
        if (stickySocialNavi($("#refScroller"), $("#refScroller").height())) {
          if (!passedMobileNavi) {
            passedMobileNavi = true;
            $("#wannabeSticky").addClass("sticky");
          }
        }
        else {
          passedMobileNavi = false;
          $("#wannabeSticky").removeClass("sticky");
        }
    
        if (stickySocialNavi($("#end"), $(window).height())) {
          var var1 = $(window).scrollTop(),
          var2 = $("#end").offset().top,
          var3 = $(window).height();
          var calculateOffset = (var2 - var3) - var1;
          $("#wannabeSticky").css("top", "calc(50% + " + calculateOffset + "px)");
        }
        else {
          $("#wannabeSticky").css("top", "50%");
        }
      });
    });
    

    For the sticky-ness to start, I took the reference point (which is the non-moving element right next to it) and its height. The sticky element gets a fixed position as long as the scrollbar goes past the reference point's center.

    As the stick element is centered, it gets additional top offset values when the end of its container is reached. It is still fixed, but its top property's value takes the scroll difference, thus slowly depicting it towards the end of the container.

    I don't know if this is the most elegant, straightforward, or easy to implement/understand solution, but it worked for me.