Bootstrap mdl 模板上的 scrollspy

Bootstrap scrollspy on mdl template

我已经从 MDL 下载了 Android.com 模板。并对其进行了编辑以删除一些功能。

但我现在要做的是添加一个新功能:Scrollspy。我希望突出显示的菜单选项在用户滚动页面时得到更新。

我从 w3schools 那里得到了 scrollspy 的代码,并尝试在我的代码上实现,但没有成功。它仍在工作,就好像我从未改变过任何东西一样。我想我可能遗漏了一些东西,我需要帮助。这是我的代码:

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="Some Content">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
    <title>Title</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en">
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
    <link rel="stylesheet" href="material.min.css">
    <link rel="stylesheet" href="styles.css">
    <!--link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"-->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <style>
    body {
      position: relative;
    }
    </style>
  </head>
  <body data-spy="scroll" data-target=".navbar" data-offset="50">
    <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
      <div class="android-header mdl-layout__header mdl-layout__header--waterfall">
        <div class="mdl-layout__header-row">
          <span class="android-title mdl-layout-title">
            <img class="android-logo-image" src="images/android-logo.png">
          </span>
          <!-- Add spacer, to align navigation to the right in desktop -->
          <div class="android-header-spacer mdl-layout-spacer"></div>
          <!-- Navigation -->
          <div class="android-navigation-container">
          <nav class="android-navigation mdl-navigation navbar navbar-inverse navbar-fixed-top">
            <div class="container-fluid">
              <div>
            <div class="collapse navbar-collapse" id="myNavbar">
              <div class="nav navbar-nav">
              <a class="mdl-navigation__link mdl-typography--text-uppercase" href="#home">Home</a>
              <a class="mdl-navigation__link mdl-typography--text-uppercase" href="#howtoplay">How to play</a>
              <a class="mdl-navigation__link mdl-typography--text-uppercase" href="#tryit">Try it</a>
              <a class="mdl-navigation__link mdl-typography--text-uppercase" href="#contribute">Contribute</a>
              <a class="mdl-navigation__link mdl-typography--text-uppercase" href="#getupdated">Stay tuned</a>
              <a class="mdl-navigation__link mdl-typography--text-uppercase" href="#contact">Contact Us</a>
            </div>
          </div>
        </div>
      </div>
    </nav>
          </div>
          <span class="android-mobile-title mdl-layout-title">
            <img class="android-logo-image" src="images/android-logo.png">
          </span>
          <button class="android-more-button mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect" id="more-button">
            <i class="material-icons">more_vert</i>
          </button>
          <ul class="mdl-menu mdl-js-menu mdl-menu--bottom-right mdl-js-ripple-effect" for="more-button">
            <li class="mdl-menu__item">5.0 Lollipop</li>
            <li class="mdl-menu__item">4.4 KitKat</li>
            <li disabled class="mdl-menu__item">4.3 Jelly Bean</li>
            <li class="mdl-menu__item">Android History</li>
          </ul>
        </div>
      </div>

      <div class="android-content mdl-layout__content">
        <a name="top"></a>
        <div class="android-be-together-section mdl-typography--text-center" id="home" class="container-fluid">
          <div class="logo-font android-slogan">think. tap. quickly.</div>
          <div class="logo-font android-sub-slogan">challenge your mind with the queen of sciences - Math</div>

          <a href="#screens">
            <button class="android-fab mdl-button mdl-button--colored mdl-js-button mdl-button--fab mdl-js-ripple-effect">
              <i class="material-icons">expand_more</i>
            </button>
          </a>
        </div>

        <div class="android-screen-section mdl-typography--text-center" id="howtoplay" class="container-fluid">
          <!-- Some content here -->
        </div>

        <div class="android-wear-section" id="tryit">
          <!-- Some Content Here-->
        </div>

        <div class="android-customized-section" id="contribute">
          <!-- Some Content Here-->
        </div>

        <div class="android-wear-section" id="getupdated">
          <!-- Some Content Here-->
        </div>

        <div class="android-more-section" id="contact">
          <!-- Some Content Here-->
        </div>
      </div>
    </div>
    <script src="https://code.getmdl.io/1.3.0/material.min.js"></script>
  </body>
</html>

我发现它在 Bootstrap 库中的 scroll 事件处理程序与 material.min.js 中的代码冲突。这似乎就是它对您不起作用的原因。

如果您包含 Bootstrap 库只是为了获得 ScrollSpy 效果,那就太过分了。

所以我修改了 https://github.com/makotot/scrollspy 的脚本,添加了一个偏移选项来解决导航的重叠 header。

演示:http://plnkr.co/edit/XKdRxWkSiuX2qWrhk8Xp?p=preview(确保页面足够宽以在顶部显示标题)

// ScrollSpy script from https://github.com/makotot/scrollspy
// Licence: MIT
// Altered by KScandrett - added a header offset (in pixels).

function ScrollSpy(wrapper, opt) {

  this.doc = document;
  this.wrapper = (typeof wrapper === 'string') ? this.doc.querySelector(wrapper) : wrapper;
  this.nav = this.wrapper.querySelectorAll(opt.nav);

  this.contents = [];
  this.win = window;
  this.winH = this.win.innerHeight;
  this.className = opt.className;
  this.callback = opt.callback;
  this.offset = opt.offset || 0; // KS added - offset to account for nav header height
  this.init();
}

ScrollSpy.prototype.init = function() {
  this.contents = this.getContents();
  this.attachEvent();
};

ScrollSpy.prototype.getContents = function() {
  var targetList = [];

  for (var i = 0, max = this.nav.length; i < max; i++) {
    var href = this.nav[i].href;

    targetList.push(this.doc.getElementById(href.split('#')[1]));
  }

  return targetList;
};

ScrollSpy.prototype.attachEvent = function() {
  this.win.addEventListener('load', (function() {
    this.spy(this.callback);
  }).bind(this));


  var scrollingTimer;

  this.win.addEventListener('scroll', (function() {
    if (scrollingTimer) {
      clearTimeout(scrollingTimer);
    }

    var _this = this;

    scrollingTimer = setTimeout(function() {
      _this.spy(_this.callback);
    }, 10);
  }).bind(this), true); // KS added "true" parameter to work with material.js


  var resizingTimer;

  this.win.addEventListener('resize', (function() {
    if (resizingTimer) {
      clearTimeout(resizingTimer);
    }

    var _this = this;

    resizingTimer = setTimeout(function() {
      _this.spy(_this.callback);
    }, 10);
  }).bind(this), true); // KS added "true" parameter to work with material.js
};

ScrollSpy.prototype.spy = function(cb) {
  var elems = this.getElemsViewState();

  this.markNav(elems);

  if (typeof cb === 'function') {
    cb(elems);
  }
};

ScrollSpy.prototype.getElemsViewState = function() {
  var elemsInView = [],
    elemsOutView = [],
    viewStatusList = [];

  for (var i = 0, max = this.contents.length; i < max; i++) {
    var currentContent = this.contents[i],
      isInView = this.isInView(currentContent);

    if (isInView) {
      elemsInView.push(currentContent);
    } else {
      elemsOutView.push(currentContent);
    }
    viewStatusList.push(isInView);
  }

  return {
    inView: elemsInView,
    outView: elemsOutView,
    viewStatusList: viewStatusList
  };
};

ScrollSpy.prototype.isInView = function(el) {
  var winH = this.winH,
    scrollTop = this.doc.documentElement.scrollTop || this.doc.body.scrollTop,
    scrollBottom = scrollTop + winH,
    rect = el.getBoundingClientRect(),
    elTop = rect.top + scrollTop - this.offset, // KS added
    elBottom = elTop + el.offsetHeight;

  return (elTop < scrollBottom) && (elBottom > scrollTop);
};

ScrollSpy.prototype.markNav = function(elems) {
  var navItems = this.nav,
    isAlreadyMarked = false;

  for (var i = 0, max = navItems.length; i < max; i++) {
    if (elems.viewStatusList[i] && !isAlreadyMarked) {
      isAlreadyMarked = true;
      navItems[i].classList.add(this.className);
    } else {
      navItems[i].classList.remove(this.className);
    }
  }
};

您可以像下面这样使用它:

<div id="js-scrollspy">
  <ul class="js-scrollspy-nav">
    <li><a href="#link1">link 1</a></li>
    <li><a href="#...">...</a></li>
    <li><a href="#...">...</a></li>
    <li><a href="#...">...</a></li>
  </ul>
  ...
  <div>
    <div id="link1"></div>
    ...
  </div>
</div>

<script>
   var spy = new ScrollSpy('#js-scrollspy', {
      nav: '.js-scrollspy-nav > li > a',
      offset: 64, // offset in pixels for header overlap
      className: 'is-inview',
      callback: function () {} 
   });
</script>