Bootstrap 3.0 scrollspy响应偏移

时间:2013-08-14 23:50:46

标签: twitter-bootstrap responsive-design twitter-bootstrap-3 scrollspy

我正在尝试让Bootstrap的scrollspy在响应式网站上可靠地工作,其中顶部导航栏的高度根据媒体/浏览器的宽度而变化。因此,我没有对data-offset属性上的偏移量进行硬编码,而是通过Javascript初始化动态设置它,如下所示:

$('body').scrollspy({ offset: 70, target: '#nav' });

对于宽布局(即Bootstrap -lg),它可以正常工作,但对于较窄的布局,似乎存在“累积”偏移。换句话说,它适用于第一部分,但随后需要增加像素来激活以下部分(例如,下一部分为90px,第三部分为110px等)。

我尝试直接操作scrollspy对象,如下所示: How do I set the offset for ScrollSpy in Bootstrap?但无济于事。

有人可以推荐在响应式网站中实施scrollspy的规范方法,其中偏移量会根据媒体宽度而变化吗?

其他信息:

我刚刚分析了两个场景中的scrollspy对象,结果发现当通过data-属性而不是通过JS初始化时,偏移列表是不同的。看起来当我通过JS初始化它时,偏移数组会在一些BS响应调整发生之前填充,因此高度不同。在所有响应式内容运行后,如何触发scrollspy的初始化? BS调整布局后是否有钩子/回调? JS是否涉及或者是CSS处理的所有响应式内容?

3 个答案:

答案 0 :(得分:17)

不幸的是,当第一次调用data-*函数时,jQuery只会引入$().data(...)个属性。并且scrollspy仅在初始化时读取选项一次。 scrollspy的refresh函数不会重新加载任何选项。在同一元素上再次调用$(..).scrollspy(...)将忽略任何新数据选项(使用先前初始化的值)。

然而,scrollspy 将选项值存储在键'bs.scrollspy'下的元素数据中。因此,您可以更改该数据密钥中的options.offset字段。

为了考虑动态变化的导航栏高度以及需要更改scrollspy偏移值,您可以将以下示例用于可变高度固定顶部导航栏。

以下是一些事情。

  • 它通过javascript初始化scrollspy(在window.load事件触发后),并以导航栏当前高度的偏移量开始(也将主体的padding-top值调整为与导航栏高度相同)。
  • 调整调整大小事件并调整正文padding-top,并调整scrollspy offset以匹配。然后执行refresh以重新计算锚点偏移量。

HTML

<body>
  <style type="text/css">
    /* prevent navbar from collapsing on small screens */
    #navtop .navbar-nav > li { float: left !important; }
  </style>
  <nav id="navtop" class="navbar-fixed-top" role="navigation">
    <div class="container">
      <ul class="nav nav-pills navbar-nav">
        <li><a href="#one">One</a></li>
        <li><a href="#two">Two</a></li>
        <li><a href="#three">Three</a></li>
      ... some more navlinks, or dynamically added links that affect the height ...
      </ul>
    </div>
  </nav>
  <section id="one">
   ...
  </section>
  <section id="two">
   ...
  </section>
  <section id="three">
   ...
  </section>
   ....
</body>

的JavaScript

$(window).on('load',function(){
    var $body   = $('body'), 
        $navtop = $('#navtop'),
        offset  = $navtop.outerHeight();

    // fix body padding (in case navbar size is different than the padding)
    $body.css('padding-top', offset);
    // Enable scrollSpy with correct offset based on height of navbar
    $body.scrollspy({target: '#navtop', offset: offset });

    // function to do the tweaking
    function fixSpy() {
        // grab a copy the scrollspy data for the element
        var data = $body.data('bs.scrollspy');
        // if there is data, lets fiddle with the offset value
        if (data) {
            // get the current height of the navbar
            offset = $navtop.outerHeight();
            // adjust the body's padding top to match
            $body.css('padding-top', offset);
            // change the data's offset option to match
            data.options.offset = offset;
            // now stick it back in the element
            $body.data('bs.scrollspy', data);
            // and finally refresh scrollspy
            $body.scrollspy('refresh');
        }
    }

    // Now monitor the resize events and make the tweaks
    var resizeTimer;
    $(window).resize(function() {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(fixSpy, 200);
    });
});

就是这样。不漂亮,但绝对有效。我上面的HTML可能需要一些调整。

如果您动态向导航栏添加元素,则需要在插入后调用fixSpy()。或者您可以通过fixSpy()计时器呼叫setInterval(...)以便一直运行。

答案 1 :(得分:2)

Scrollspy在初始化后设置目标/偏移列表。如果您调整屏幕大小,则不会再次初始化scrollspy。您将重新加载页面以重新计算偏移量。

您提到的“累积”偏移量是由具有不同内容高度的相同偏移列表引起的。

也可以使用$(window).resize()触发重新加载注意某些浏览器会触发此操作两次,请参阅https://stackoverflow.com/a/4298653/1596547获取解决方案:

var id; 

$(window).resize(function() 
{
    clearTimeout(id);
    id = setTimeout($('body').scrollspy({ target: '' }), 500);
});

请注意http://getbootstrap.com/javascript/#scrollspy上的文档会告诉您类似的内容:

  

“将scrollspy与添加或删除一起使用时   来自DOM的元素,你需要调用刷新方法“

通过上述内容,您将得到类似的内容:

var id; 

$(window).resize(function() 
{
    clearTimeout(id);
    id = setTimeout($('[data-spy="scroll"]').each(function () {var $spy = $(this).scrollspy('refresh'}), 500);
});

注意:scollspy插件使用jQuery的scrollTop()函数进行计算。所以也请阅读:http://blog.jonathanargentiero.com/?p=134

答案 2 :(得分:0)

我知道最初的问题要求BS3,但是我没有找到BS4的正确且简单的答案。因此,我现在附上最适合我的解决方案。

从BS4开始,配置位置已更改。如果您正在寻找正确的位置以直接即时更改offset。它是:$('body').data('bs.scrollspy')._config.offset。另外,如果您希望直接更改生效。之后致电$('body').scrollspy('refresh');。现在,offsetspy会考虑偏移量。

基于这种方法,我写了一个小片段,可以帮助您针对特定的导航容器(例如BS4导航栏)动态调整偏移量。

var initScrollSpy = function() {
  var navContainer = '#mainNav'; // your navigation container

  // initial bind of scrollspy
  var bindScrollSpy = function() {
      $('body').scrollspy({
          target: navContainer,
          offset: getOffset() // determine your offset
      });
  }

  // get your offset dynamically
  var getOffset = function() {
      return $(navContainer).outerHeight();
  };

  // update the offset but only if the container size changed
  var updateOffset = function() {
      var cfg = $('body').data('bs.scrollspy')._config;
      if(cfg.offset != getOffset()){
          cfg.offset = getOffset();
          $('body').scrollspy('refresh');
      }
  }

  bindScrollSpy(); // bind scrollspy 
  $(window).resize(updateOffset); // react on resize event
  $(".collapse").on('shown.bs.collapse', updateOffset); // react on BS4 menu shown event (only on mobile). You might omit this line if your navigation has no change in height when opened on mobile
};

initScrollSpy(); // finally call snippet

我在代码中附加了代码段的说明。但是通常我会像平常一样绑定scrollspy偏移量。此外,我们有两个事件。对窗口调整大小有反应的一种,对导航栏有反应的一种完全显示。对于这两种情况,我都会检查偏移量值是否与所需的偏移量不同,然后直接在scrollspy中更新它。