Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get dynamic data-offset values for Bootstrap 3 affix method

I would like to use the Affix method described in Bootstraps documentation (http://getbootstrap.com/javascript/#affix), however the navbar I would like to fix to the top of the page after it scrolls to it can have different offset values depending upon the content above it.

Here's an example of the navbar:

<div class="navbar navbar-default" data-spy="affix" data-offset-top="200">
  <ul class="nav navbar-nav">
    <li class="active"><a href="#">Link</a></li>
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
  </ul>
</div>

As you can see, the data-offset-top is currently set at 200. This works fine if the content above is 200px tall, but the content above is dynamic and so the height above this navbar isn't always the same. How can I make the vale for data-offset-top be dynamic?

I'm guessing I'll have to use the javascript way of doing it but I'm nit sure.

like image 650
bigmike7801 Avatar asked Sep 09 '13 16:09

bigmike7801


3 Answers

You could use jQuery to get the dynamic content height above the navbar. For example:

$('#nav').affix({
      offset: {
        top: $('header').height()
      }
}); 

Working demo: http://bootply.com/69848

In some cases, offset.bottom must also be calculated to determine when to "un-affix" the element. Here's an example of affix-bottom

like image 134
Zim Avatar answered Nov 02 '22 04:11

Zim


I was absolutely frustrated with the way this works, every time doing some custom calculation and building the offset off the the heights of the elements above the desired affix.

Here's affix at it's best.

The normal boostrap affix doesn't take the items natural offset top position into account, you have to manually pass it in as an attribute. This takes care of that, and accounts for the offset top changing on window resize.

var $attribute = $('[data-smart-affix]');
$attribute.each(function(){
  $(this).affix({
    offset: {
      top: $(this).offset().top,
    }
  })
})
$(window).on("resize", function(){
  $attribute.each(function(){
    $(this).data('bs.affix').options.offset.top = $(this).offset().top
  })
})

I also posted this on code review.

Don't forget to add the data-smart-affix attribute to your affix element

Try this sample code: https://jsfiddle.net/NabiKAZ/qyrauogw/
(In this sample, the height of red section may be 100px or 200px or 300px in diffrence width of window, So need to diffrence data-offset-top when scrolled.)

like image 23
ThomasReggi Avatar answered Nov 02 '22 03:11

ThomasReggi


ThomasReggi's data-smart-affix didn't work for me in some corner cases (when resizing the document, or when the affixed column was too tall). But it gave me a simple idea: Add an empty element before the affixed content, and use that to get the offset. Works well when resized.

While I was at it, I also set the width of the element to be it's natural width in the original parent. Here's my solution:

$('[data-affix-after]').each( function() {
  var elem = $(this);
  var parent_panel = elem.parent();
  var prev = $( elem.data('affix-after') );

  if( prev.length != 1 ) {
    /* Create a new element immediately before. */
    prev = elem.before( '<div></div>' ).prev();
  }

  var resizeFn = function() {
      /* Set the width to it's natural width in the parent. */
      var sideBarNavWidth = parent_panel.width()
          - parseInt(elem.css('paddingLeft'))
          - parseInt(elem.css('paddingRight'))
          - parseInt(elem.css('marginLeft'))
          - parseInt(elem.css('marginRight'))
          - parseInt(elem.css('borderLeftWidth'))
          - parseInt(elem.css('borderRightWidth'));
      elem.css('width', sideBarNavWidth);

      elem.affix({
          offset: {
              top: prev.offset().top + prev.outerHeight(true),
              bottom: $('body>footer').outerHeight(true)
          }
      });
  };

  resizeFn();
  $(window).resize(resizeFn);
});

The HTML that goes with it is:

<div id='before-affix'></div><!-- leave this empty -->
<div data-affix-after='#before-affix'>
    Put content to affix here.
</div>

or

<div data-affix-after='#create'><!-- any ID that doesn't exist -->
    Put content to affix here.
</div>

The required CSS is:

.affix { top: 0px },
.affix-bottom { position: absolute; }

If you want to disable affix (e.g. when the right column is stacked), then use the CSS:

.affix, .affix-bottom { position: static; }

inside the appropriate media query.

GI

like image 3
gi1242 Avatar answered Nov 02 '22 04:11

gi1242