twitter-bootstrap Bootstrap 3.0 scrollspy 响应偏移

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18244186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 17:58:59  来源:igfitidea点击:

Bootstrap 3.0 scrollspy responsive offsets

twitter-bootstrapresponsive-designtwitter-bootstrap-3scrollspy

提问by Ike

I'm trying to get Bootstrap's scrollspy to work reliably on a responsive site on which the top navbar's height changes according to the width of the media/browser. So instead of hardcoding the offset on the data-offsetattribute I'm setting it dynamically through Javascript initialization like this:

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

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

For wide layouts (i.e. Bootstrap -lg) it works fine but for narrower layouts there seems to be an "accumulating" offset in effect. In other words, it works fine for the first section but then takes increasing pixels to activate the following ones (e.g. 90px for the next, 110px for the 3rd, etc.).

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

I tried manipulating the scrollspy object directly as mentioned in this answer: How do I set the offset for ScrollSpy in Bootstrap?but to no avail.

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

Can someone recommend a canonical way of implementing scrollspy in a responsive site where the offset varies according to the media width?

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

Additional Information:

附加信息:

I just analyzed the scrollspy object in both scenarios and it turns out that the list of offsets is different when it's initialized through data-attributes only vs. via JS. It seems like when I initialize it via JS the offsets array gets populated before some of BS responsive adjustments happen and therefore the heights are different. How can I trigger scrollspy's initialization afterall the responsive stuff has run? Is there a hook/callback after BS is done adjusting the layout? Is JS even involved or is all the responsive stuff handled by CSS?

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

回答by Troy Morehouse

Unfortunately jQuery pulls in the data-*attributes only once when the $().data(...)function is called the first time. And scrollspyonly reads the options once when it is initialised. The refreshfunction of scrollspy does not reload any of the options. Calling $(..).scrollspy(...)again on the same element ignores any new data options (uses the previously initialized values).

不幸的是,jQuerydata-*$().data(...)在第一次调用函数时拉入属性一次。并且scrollspy仅在初始化时读取选项一次。该refreshscrollspy的功能不装任何的选项。$(..).scrollspy(...)再次调用同一元素会忽略任何新的数据选项(使用先前初始化的值)。

However, scrollspy doesstore the options values in the elements data under the key 'bs.scrollspy'. So you can alter the options.offsetfield inside that data key.

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

To account for a dynamically changing navbar height and the need to alter the scrollspy offset value, you can use the following example for a variable height fixed-top navbar.

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

The following does a few of things.

下面做了一些事情。

  • It initializes scrollspy via javascript (after the window.loadevent fires), and starts off with an offset of the navbar's current height (also adjusts the body's padding-topvalue to be the same as the navbar height).
  • Resize events are monitored and the body's padding-topis adjusted, and the scrollspy offsetis tweaked to match. Then a refreshis performed to recalculate the anchor offsets.
  • 它通过 javascript 初始化 scrollspy(在window.load事件触发后),并以导航栏当前高度的偏移量开始(还将主体的padding-top值调整为与导航栏高度相同)。
  • 调整大小事件受到监控并padding-top调整身体,并offset调整滚动间谍以匹配。然后refresh执行 a 以重新计算锚点偏移量。

HTML

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

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

And thats it. Not pretty, but it definitely works. My HTML above may need some tweaking.

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

If you add elements to your navbar dynamically you will need to call fixSpy()after they are inserted. Or you could call fixSpy()via a setInterval(...)timer to run all the time.

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

回答by Bass Jobsen

Scrollspy set a list of targets / offset after initialization. If you resize your screen scrollspy is not initialized again. You will have reload your page to recalculate the offsets.

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

The "accumulating" offset in effect you mention is cause by the same list of offsets with different content heights.

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

To could also trigger this reload with $(window).resize()note some browser will fire this twice, see https://stackoverflow.com/a/4298653/1596547for a solution:

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

var id; 

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

Note the docs on http://getbootstrap.com/javascript/#scrollspytell you something similar:

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

"When using scrollspy in conjunction with adding or removing of elements from the DOM, you'll need to call the refresh method "

“当将 scrollspy 与从 DOM 中添加或删除元素结合使用时,您需要调用 refresh 方法”

With the above, you will get something like:

有了上面的,你会得到类似的东西:

var id; 

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

NOTE: The scollspy plugin uses jQuery's scrollTop() function for calculations. So also read this: http://blog.jonathanargentiero.com/?p=134.

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

回答by thex

I'm aware that the original question asks for BS3 but I did not find a correct and easy answer for BS4. Hence I'm now attaching my solution which works perfectly for me.

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

As of BS4 the configuration location changed. If you are searching for the correct spot to directly change the offseton the fly. Here it is: $('body').data('bs.scrollspy')._config.offset. Additionally if you want that the direct change takes effect. Call $('body').scrollspy('refresh');afterwards. Now the offset is respected by scrollspy.

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

Based on this approach I wrote a little snippet which might help you in adapting the offset dynamically for a specific navigation container (e.g. BS4 navbar).

基于这种方法,我编写了一个小片段,它可能会帮助您针对特定导航容器(例如 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

I attached the explanations of the snippet within the code. But generally I bind the scrollspy offset as you normally would do. Additionally, we have two events. One that reacts on window resize and one that reacts on navbar fully shown. In case of both events I check if the offset value differs from the desired one and update it directly within scrollspy and that's it.

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