【问题标题】:Bootstrap 3.0 scrollspy responsive offsetsBootstrap 3.0 scrollspy 响应式偏移
【发布时间】:2013-08-14 23:50:45
【问题描述】:

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

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

对于宽布局(即 Bootstrap -lg),它工作得很好,但对于较窄的布局,似乎有一个“累积”的偏移量。换句话说,它在第一部分工作得很好,但随后需要增加像素来激活后面的部分(例如,下一个部分是 90 像素,第三部分是 110 像素,等等)。

我尝试按照此答案中的说明直接操作 scrollspy 对象: How do I set the offset for ScrollSpy in Bootstrap? 但无济于事。

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

附加信息:

我刚刚分析了这两种情况下的 scrollspy 对象,结果发现仅通过 data- 属性与通过 JS 进行初始化时,偏移列表是不同的。似乎当我通过 JS 初始化它时,偏移量数组在一些 BS 响应调整发生之前被填充,因此高度不同。 在所有响应的东西都运行之后,我如何触发 scrollspy 的初始化? BS 调整布局后是否有挂钩/回调?是否涉及到 JS,或者所有响应式的东西都由 CSS 处理?

【问题讨论】:

  • 有趣的是,如果我让 scrollspy 从 data- 属性初始化自己,它工作正常。只有当我从 Javascript 中执行此操作时,我才会获得“累积”偏移效果。以某种方式从 JS 初始化 scrollspy 就像在上面的 sn-p 中并不等同于让它从 data- 属性中进行。还没弄清楚为什么。
  • 响应性仅由 CSS 处理(除非您在 IE8 中具有 respond.js 以实现兼容性)。您是否将 js 代码包装在文档就绪处理程序中?
  • 是的,我在$(document).ready(function() { 内打电话给$('body').scrollspy({...})。但正如我上面提到的,与使用data- 属性自行初始化相比,这样做会创建一组不同的偏移量。
  • 也许 $(document).ready 在这里有所作为。该插件使用$(window).on('load' 设置数据属性。另见:stackoverflow.com/questions/4395780/…
  • 我认为你是对的,这可能是关键。根据该评论,$(document).ready 比$(window) 加载事件更早(通常更早)发生。所以很可能高度还没有完全调整。一种选择是尝试在后一个回调上挂钩偏移量,但此时我选择了一种完全不同的方法,调整 CSS 填充等。谢谢大家的帮助和见解。

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


【解决方案1】:

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

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

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

下面做了一些事情。

  • 它通过 javascript 初始化 scrollspy(在window.load 事件触发之后),并从导航栏当前高度的偏移量开始(同时将主体的 padding-top 值调整为与导航栏高度相同)。李>
  • 监控调整大小事件并调整正文的padding-top,并调整滚动间谍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()。或者您可以通过setInterval(...) 计时器调用fixSpy() 以一直运行。

【讨论】:

  • 很好,它有效,但为什么 setTimeout()?我已经尝试过类似的修复,只是发现当我的 JS 开始更改数据对象中的偏移量时,它还不存在。虽然使用你的代码似乎可以解决这个问题。
  • setTimeout 用于“消除/限制”在调整窗口大小时浏览器触发的调整大小事件的爆炸。如果没有设置的超时时间,根据浏览器的不同,对于较大的文档,实际调整窗口大小可能会变得缓慢。
【解决方案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。

【讨论】:

  • 谢谢他详细的回复和信息巴斯。然而,就我而言,DOM 没有改变,窗口也没有调整大小。我只有一个导航栏,它针对不同的视口宽度以不同的高度呈现。因此,如果您在宽屏桌面上加载页面,导航栏的高度为 90 像素。在 iPad 上,它是 70px 高,对于智能手机,它是 60px。这一切都发生在响应式 CSS 中。所以我需要为这些场景中的每一个设置具有不同偏移量的 scrollspy。从 JS 执行此操作失败,我上面描述的问题。
【解决方案3】:

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

从 BS4 开始,配置位置发生了变化。如果您正在寻找正确的位置以即时更改offset。这是:$('body').data('bs.scrollspy')._config.offset。此外,如果您希望直接更改生效。之后致电$('body').scrollspy('refresh');。现在,scrollspy 尊重偏移量。

基于这种方法,我编写了一个小 sn-p,它可以帮助您为特定导航容器(例如 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

我在代码中附上了sn-p的解释。但通常我会像往常一样绑定 scrollspy 偏移量。此外,我们还有两个活动。一种对窗口调整大小作出反应,另一种对导航栏作出反应,完全显示。在这两个事件的情况下,我检查偏移值是否与所需的不同,并直接在 scrollspy 中更新它,就是这样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    • 2018-10-17
    • 1970-01-01
    相关资源
    最近更新 更多