Bootstrap 3.0 scrollspy responsive offsets

会有一股神秘感。 提交于 2019-12-03 07:59:54

Unfortunately jQuery pulls in the data-* attributes only once when the $().data(...) function is called the first time. And scrollspy only reads the options once when it is initialised. The refresh function 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).

However, scrollspy does store the options values in the elements data under the key 'bs.scrollspy'. So you can alter the options.offset field inside that data key.

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.

The following does a few of things.

  • It initializes scrollspy via javascript (after the window.load event fires), and starts off with an offset of the navbar's current height (also adjusts the body's padding-top value to be the same as the navbar height).
  • Resize events are monitored and the body's padding-top is adjusted, and the scrollspy offset is tweaked to match. Then a refresh is performed to recalculate the anchor offsets.

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

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

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.

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.

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/1596547 for a solution:

var id; 

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

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

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!