粘性页眉-带有标签的滚动

时间:2019-03-06 07:41:28

标签: javascript jquery html nav sticky

我的粘性标头的行为存在问题。

  

期望的行为:

     

a)滚动到.nav底部碰到section顶部的位置   标签上的active类,它将保持active.nav到达下一部分的顶部。

     

b)点击相应的   部分的.tab始终将您导航到该部分的顶部,并将active类添加到选项卡。

因此,通过滚动或单击,选项卡的active状态始终保持到.nav进入下一个section,在这种情况下为active状态转到该部分的标签等。

测试问题:

1)中途向下滚动Option Two的{​​{1}}的{​​{1}}状态。

2)使用active滚动到.tab的顶部,而不是所选scrollTop的顶部。

.container
section
class StickyNavigation {
  constructor() {
    this.currentId = null;
    this.currentTab = null;
    let self = this;
    $(".tab").click(function() {
      self.onTabClick(event, $(this));
    });
    $(".container").scroll(() => {
      this.onScroll();
    });
    $(".container").resize(() => {
      this.onResize();
    });
  }
  /*Scrolls down to Tab selection*/
  onTabClick(event, element) {
    event.preventDefault();
    let scrollTop = $(element.attr("href")).offset().top;
    if (!$(".nav").hasClass("nav--top")) {
      scrollTop = scrollTop;
    }
    $(".container").animate({
      scrollTop: scrollTop
    }, 600);
  }
  onScroll() {
    this.navPosition();
    this.tabAnimation();
  }
  navPosition() {
    let offset = $(".sticky").offset().top + $(".sticky").height();
    if ($(".container").scrollTop() > offset) {
      $(".nav").addClass("nav--top");
    } else {
      $(".nav").removeClass("nav--top");
    }
  }
  tabAnimation() {
    $("section").each(function() {
      var actual = $(this),
        actualHeight = actual.height(),
        actualAnchor = $(".sticky").find('a[href="#' + actual.attr("id") + '"]');
      if (
        actual.offset().top <= $(".container").scrollTop() &&
        actual.offset().top + actualHeight > $(".container").scrollTop()
      ) {
        actualAnchor.addClass("active");
      } else {
        actualAnchor.removeClass("active");
      }
    });
  }
}
new StickyNavigation();

2 个答案:

答案 0 :(得分:3)

如果您能够调整自己的html结构,我想这可能对您有所帮助。 我在我的一个项目中做了这样的事情,它就来了:

您需要借助.getBoundingClientRect()检出this

来过滤出哪个元素正在触及标题的底部

并检查标题顶部右上方是否有任何内容。

我借助filter方法来查找内置函数中的内容索引,并设置了相应锚点的.active类。

更新:因为您有固定的标头和粘性标头。并且它们之间有一个 hero 部分,我将第一个标头的position设置为fixed,然后添加相同的高度(在本例中为100px)像padding-top上的body一样,因此hero元素不会被裁剪掉。

现在,由于我们知道第一个固定标头的高度为sticky,因此粘性标头的位置为top,并且100px设置为100px

现在,脚本应检查固定页眉高度粘性页眉 Y 轴位置>

现在来看一下代码片段:

var direction = 0; // a variable to keep track of scrolled position;
$(window).on('scroll', function() {
  var headerElBottom = document.getElementById('side-nav').getBoundingClientRect().y + document.getElementById('header').getBoundingClientRect().height;
  // check if window is scrolling up or down;
  if ($(window).scrollTop() > direction) { // if true, window scrolling scrolling down;
    $('#side-nav').find('a').removeClass('active'); // remove active class from all anchors
    $('#side-nav').find('a').eq(
      // .eq() selector helps to find elements with index number, and here we pass a filter to find the content that is within the viewport;
      $('#content').find('div').filter(function(index) {
        return this.getBoundingClientRect().y <= headerElBottom && this.getBoundingClientRect().y + this.getBoundingClientRect().height > headerElBottom;
      }).index()
    ).addClass('active');
    // update the current scroll position now;
    direction = $(window).scrollTop();

  } else { // if false, window scrolling scrolling up;
    $('#side-nav').find('a').removeClass('active'); // remove active class from all anchors
    $('#side-nav').find('a').eq(
      $('#content').find('div').filter(function(index) {
        return this.getBoundingClientRect().y < headerElBottom && this.getBoundingClientRect().y + this.getBoundingClientRect().height > headerElBottom;
      }).index()
    ).addClass('active');
    // update the current scroll position now;
    direction = $(window).scrollTop();
  }
});
* {
  margin: 0;
  padding: 0;
}

body {
  padding-top: 100px;
}

#side-nav {
  /* feel free to remove or change, only for testing */
  position: sticky;
  width: 100%;
  top: 100px;
  padding: 35px;
  border-bottom: 1px solid black;
  background-color: #fff;
  box-sizing: border-box;
}

.header {
  padding: 40px;
  background-color: #cecece;
  position: fixed;
  top: 0;
  height: 100px;
  width: 100%;
  box-sizing: border-box;
  font-size: 20px;
}

.hero {
  padding: 60px 40px;
  background-color: #967373;
  height: 200px;
  box-sizing: border-box;
  font-size: 20px;
}

#side-nav a {
  /* feel free to remove, only for testing */
  text-decoration: none;
  color: grey;
  margin-right: 5px;
  font-size: 24px;
  font-weight: bold;
}

#side-nav a.active {
  color: red;
  /* sets color for the default active class */
}

#content div {
  min-height: 900px;
  background-color: #cecece;
  border-bottom: 1px solid #fff;
  box-sizing: border-box;
  padding: 100px 50px;
}

#content div:nth-child(2) {
  background-color: #e25454;
}

#content div:nth-child(3) {
  background-color: #5d5dff;
}

#content div:nth-child(4) {
  background-color: #4db14d;
}

#content div:nth-child(5) {
  background-color: #cece52;
}

#content div:nth-child(6) {
  background-color: #cc9023;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header" id="header">Fixed Header</div>
<div class="hero">Hero section</div>
<div id="side-nav">
  <a href="#div1" id="div1a" class='active'>One</a>
  <!-- set a default class assuming the first one will be in viewport while window loads -->
  <a href="#div2" id="div2a">Two</a>
  <a href="#div3" id="div3a">Three</a>
  <a href="#div4" id="div4a">Four</a>
  <a href="#div5" id="div5a">Five</a>
  <a href="#div6" id="div6a">Six</a>
</div>
<div id="content">
  <div id="div1">
    <p>One</p>
  </div>
  <div id="div2">
    <p>Two</p>
  </div>
  <div id="div3">
    <p>Three</p>
  </div>
  <div id="div4">
    <p>Four</p>
  </div>
  <div id="div5">
    <p>Five</p>
  </div>
  <div id="div6">
    <p>Six</p>
  </div>
</div>

答案 1 :(得分:2)

您的部分滚动到顶部的原因是由于添加和 去除粘性元素。使用包装纸代替粘纸,以始终占据 粘性元素是否存在。

class StickyNavigation {
  constructor() {
    this.currentId = null;
    this.currentTab = null;
    this.setup();
    this.onResize();
    let self = this;
    $(".tab").click(function(event) {
      self.onTabClick(event, $(this));
    });
    $(".container").scroll(() => {
      this.onScroll();
    });
    $(window).resize(() => {
      this.onResize();
    });
  }
  setup() {
    this.$sticky = $('.sticky');
    window.stk = this.$sticky;
    this.$stickyWrap = $('<div>').insertAfter(this.$sticky);
    this.$sticky.appendTo(this.$stickyWrap);
  }
  /*Scrolls down to Tab selection*/
  onTabClick(event, element) {
    event.preventDefault();
    let $targetElement = $(element.attr("href"));
    let positionTop = $targetElement.position().top;
    let scrollTop = $('.container').scrollTop();
    $(".container").animate({
      scrollTop: scrollTop - this.stickyOuterHeight + positionTop
    }, 600);
  }
  onScroll() {
    this.navPosition();
    this.tabAnimation();
  }
  onResize() {
    this.stickyOuterHeight = this.$sticky.outerHeight();
    this.$sticky.width(this.$stickyWrap.width());
    this.$stickyWrap.css('minHeight', this.stickyOuterHeight);
  }
  navPosition() {
    if (this.$stickyWrap.position().top < 0) {
      this.$sticky.addClass("fixed");
    } else {
      this.$sticky.removeClass("fixed");
    }
  }
  tabAnimation() {
    let desiredSpace = this.stickyOuterHeight + 10;
    $("section").each(function() {
      let actual = $(this),
        actualHeight = actual.height(),
        actualAnchor = $(".sticky").find('a[href="#' + actual.attr("id") + '"]');
      let actualTop = actual.position().top;
      let actualBottom = actualTop + actualHeight;
      if (actualTop < desiredSpace && actualBottom > desiredSpace) {
        actualAnchor.addClass("active");
      } else {
        actualAnchor.removeClass("active");
      }
    });
  }
}
$(function() {
  new StickyNavigation();
});
body {
  position: fixed;
  display: flex;
  flex-direction: column;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
}

section {
  height: 600px;
  border: 2px solid white;
  background: blue;
}

section:nth-child(2) {
  background: red;
}

.container {
  flex: 1;
  display: flex;
  position: relative;
  flex-direction: column;
  overflow: auto;
}

.long {
  height: 1200px;
}

.header {
  height: 75px;
  background: green;
}

.hero {
  background: silver;
  flex: 0;
  border: 1px solid;
}

.nav {
  background: white;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.sticky {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

.sticky.fixed {
  position: fixed;
  top: 83px;
}

.tab {
  padding: 30px 45px;
  position: relative;
}

.tab.active {
  background: #6567c5;
  color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
  <h1>Header</h1>
</div>
<div class="container">
  <div class="hero">
    <h1>Hero</h1>
  </div>
  <div class="sticky">
    <nav role="navigation" class="nav">
      <a class="tab" href="#One">Option One</a>
      <a class="tab" href="#Two">Option Two</a>
    </nav>
  </div>
  <div class="main">
    <section id="One">
      Content One
    </section>
    <section class="long" id="Two">
      Content Two
    </section>
  </div>
</div>