粘性标题 - 使用标签滚动

Posted

技术标签:

【中文标题】粘性标题 - 使用标签滚动【英文标题】:Sticky Header - Scroll with Tabs 【发布时间】:2019-07-27 19:30:05 【问题描述】:

我的粘性标题的行为有问题。

期望的行为:

a) 滚动到.nav 的底部与section 的顶部相接的位置添加 将active 类添加到选项卡,并且它保持active 直到.nav 到达下一部分的顶部。

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

因此,无论是通过滚动还是单击,选项卡的 active 状态始终保持不变,直到 .nav 进入下一个 section,在这种情况下,active 状态将转到该部分的选项卡等。

测试问题:

1) 中途向下滚动 Option Two 时,active 状态 .tab 丢失。

2) 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();
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;


.nav--top 
  position: fixed;
  top: 75px;


.sticky 
  background: white;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  position: relative;


.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">
    </section>
    <section class="long" id="Two">
    </section>
  </div>
</div>
</div>

【问题讨论】:

可以包含任何额外的库吗?或者您正在寻找仅 jquery 作为唯一依赖项的答案? 【参考方案1】:

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

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>

【讨论】:

谢谢穆尼姆。您的解决方案效果很好,但我在 Firefox 中进行测试时收到错误消息。 "message": "ReferenceError: event is not defined", "filename": "stacksnippets.net/js", "lineno": 115, "colno": 7 现在可以使用了。选项卡单击事件处理程序缺少事件参数。

以上是关于粘性标题 - 使用标签滚动的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 中滚动带有粘性标题的堆叠容器?

带有滚动背景的粘性标题

粘性表格标题滚动问题

带有平滑滚动的粘性导航正在跳跃

使用粘性导航栏自动滚动 Flexbox 列

位置:粘性 - 结合 javascript 高度调整时滚动弹跳