如何获取Bootstrap 3 affix方法的动态数据偏移值

32

我想使用Bootstrap文档中描述的Affix方法(http://getbootstrap.com/javascript/#affix),但是当滚动到导航栏后,我希望将其固定在页面顶部,而导航栏的偏移值可能会因其上方的内容而异。

以下是导航栏的示例:

<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>

如您所见,data-offset-top 当前设置为 200。如果上方的内容高度为 200 像素,则此设置正常工作,但是上方的内容是动态的,因此此导航栏上方的高度并不总是相同的。我该如何使 data-offset-top 的值变得动态?

我猜我需要使用 JavaScript 来实现它,但我不确定。


我需要一种在页面调整大小时重新计算值的方法,并添加一个空间占位符,以使附加运行平稳,例如内容固定时不会出现页面跳动。解决方案-> https://dev59.com/Qn3aa4cB1Zd3GeqPXgrF#47429990 - Hrvoje Golcic
7个回答

44

你可以使用 jQuery 获取 navbar 上方的动态内容高度。例如:

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

工作示例:http://bootply.com/69848

在某些情况下,还必须计算offset.bottom以确定何时“取消固定”元素。 这是一个affix-bottom的示例


2
使用.outerHeight()代替.height()以包括边框和填充。 - Alicia

22

我对它的工作方式感到非常沮丧,每次都要进行一些自定义计算并构建偏移量,以达到所需附加元素上方元素的高度。

这是最好的固定附加元素的方法。

普通的 Bootstrap 固定附加元素不考虑元素的自然偏移顶部位置,您需要手动将其作为属性传递。这个方法会解决这个问题,并考虑到窗口大小调整时偏移顶部发生的变化。

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
  })
})

我也在代码审查上发布了这个问题。

不要忘记将data-smart-affix属性添加到您的固定元素中。

尝试此示例代码:https://jsfiddle.net/NabiKAZ/qyrauogw/
(在此示例中,红色部分的高度可能是窗口宽度的100px、200px或300px,因此需要在滚动时区分data-offset-top。)


3
你是我的英雄!谢谢,伙计。顺便提醒其他人:在使用这段代码时不要忘记给你的固定元素添加"data-smart-affix"属性。;) - Mike
2
最佳答案!干得好。这帮助我解决了我的问题。 - Bogdan Le
1
你的代码有一个错误,请使用"$(this).data('bs.affix').options.offset.top"代替"$(this).data('bs.affix').options.offset"。另外,我建议使用[data-offset-top-dynamic]而不是[data-smart-affix]。 - Hrvoje Golcic

3

ThomasReggi的data-smart-affix在某些情况下(例如调整文档大小或粘贴列过高)无法正常工作。但这给了我一个简单的想法:在固定内容之前添加一个空元素,并使用它来获取偏移量。当调整大小时效果很好。

顺便一提,我还设置了元素的宽度为原始父级中的自然宽度。以下是我的解决方案:

$('[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);
});

相关的 HTML 代码如下:

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

或者

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

所需的CSS代码如下:
.affix { top: 0px },
.affix-bottom { position: absolute; }

如果您想禁用固定定位(例如当右侧栏被堆叠时),请使用以下CSS代码:

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

在适当的媒体查询内部。

GI


1

我建议您在固定的导航栏周围添加包装器,以避免出现“滚动跳跃”现象。Thomas发表了一个不错的解决方案,但它没有包括固定后折叠的空间,并且不适用于我必须使用的2.x Bootstrap版本。因此,我决定编写完全新的固定组件实现。它不需要Bootstrap,只需jQuery。这是我的第一个JavaScript,请多多关照 :) 但也许它能帮助别人。不使用Bootstrap总是很好。

function myaffix() {

var affixoffset = $('.navbar').offset().top

$('#nav-wrapper').height($(".navbar").height());

$(window).scroll(function () {

    if ($(window).scrollTop() <= affixoffset) {
        $('.navbar').removeClass('affix');
    } else {
        $('.navbar').addClass('affix');
    }
});

};

$(document).ready(function () {

   myaffix();

   $(window).on("resize", function () {
       myaffix();
   });

});

您可以在这里找到示例: http://jsfiddle.net/3ss7fk92/


0

Skelly的观点是正确的,没有更好的方法。使用“本地”bootstrap唯一可能的解决方案是通过JavaScript调用附加affix选项(如上面在https://dev59.com/N2Ml5IYBdhLWcg3wFjkC#18702972中提到的)。

特别需要注意的是,不可能使用data-offset属性来实现相同的效果(即使这样做会更方便,即使Affix文档在http://getbootstrap.com/2.3.2/javascript.html#affix中提到可以这样做)。

因此,可以编写

data-offset='{"top":42}'

但是不允许写例如:

data-offset='{"top":$("#banner").height()}'

原因是数据属性是通过 JQuery 的 .data() API 解析的,该 API 仅允许解析纯 JSON 值,详情请参见 https://api.jquery.com/data/

你的答案虽然不是动态的,但只是静态的。 - bigmike7801
是的 - 这不是一个独立的答案,而是对Skelly的答案的补充(我不能在那里评论)。我只是试图澄清,在页面上使用自定义JavaScript表达动态词缀计算是毫无意义的 - 我已经尝试了多个小时,包括调试引导程序和JQuery例程。 - haui

0

感谢ThomasReggi提供的想法。但也许更正确的写法是:

$(this).data('bs.affix').options.offset.top = $(this).offset().top

对于像我这样需要写出类似模拟信号的底部值的人来说,这很有用。

顺便说一句,我认为可以将其包装在基于setTimeout的装饰器中(有点像防抖函数)。


0

我之前遇到了很多问题,但不知为何,其他人提出的解决方案都无法奏效。我对 Bootstrap 和 JQuery 相对较新,所以可能做了一些错误的操作而没有察觉到。然而,我找到了一个非常好的解决方案,尽管它并没有严格使用 Bootstrap 所意图的 affix 功能。我保留了我的 #thisElement.affix {top: desiredFixedPosition;} CSS,但是从 #thisElement 的 HTML 标签中移除了 data-spydata-offset-top 属性。然后,我在 JQuery 中硬编码了这个值:

var newAffix = function() {
    if($("otherElementsAboveThisElement").height() <= $(window).scrollTop()) {
        $("#thisElement").addClass("affix");
    } else {
        $("#thisElement").removeClass("affix");
    }
}

$(document).ready(function() {
    $(window).resize(newAffix);
    $(window).scroll(newAffix);
}

就我所测试的情况来看,无论您调整页面大小或滚动多少次,这都可以正常工作。如果您在$(document).ready()函数中调用它的方式足够巧妙,甚至可以使菜单在高度动画期间保持正确的位置。正如所述,这并不是一个完全“Bootstrap批准”的解决方案,但它与Bootstrap网站的集成足够良好,应该适用于某些情况 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接