【问题标题】:content-visibility and sticky headers内容可见性和粘性标题
【发布时间】:2021-09-23 08:22:23
【问题描述】:

我正在使用一个粘性标题,它会在您向上滚动时立即更改为 position:fixed。为了检测向上滚动,我将当前的 scrollTop 与旧的 scrolltop 进行比较——这工作得非常好!

现在我正在为我的页脚实现内容可见性,以节省页面加载时的一些渲染时间。

$(document).ready(function(){
  var lastScrollTop = window.pageOffsetY || 0;
  var isFixed = false;
  $(window).on('scroll', _.throttle(function(){
      var scrollTop = $(window).scrollTop();
      if(scrollTop > 200 && scrollTop < lastScrollTop && !isFixed){
        console.log('stick header');
        $('.header').addClass('header-fixed');
        isFixed = true;
      } else if((scrollTop > lastScrollTop && isFixed) || scrollTop == 0){
        console.log('unstick header');
        $('.header').removeClass('header-fixed');
        isFixed = false;
      }
    lastScrollTop = scrollTop;
  }, 500));
});
.content {
  height: 200vh;
  background-color: grey;
}

.content-visibility {
  content-visibility: auto;
}

.content-inner {
  height: 100px;
  background-color: green;
}

img.content-inner {
  height: auto;
}

.content.content-visibility {
  height: auto;
  background-color: #ddd;
}

.footer {
  background-color: #eee;
}

.header {
  height: 200px;
  background-color: white;
}


.header-fixed {
  position: fixed;
  top: 0;
  width: 100vw;
  left: 0;
  z-index: 10;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

<div class="container">
  <div class="header">
    header
  </div>
  <div class="content">
    content 1
  </div>
  <div class="content-visibility">
     <img loading="lazy" class="content-inner" src="https://via.placeholder.com/301x310">
   <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x303">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x340">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/303x340">
        <div style="height:200px"></div>

    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/304x340">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/305x340">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/306x340">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/307x340">
  </div>
  <div class="content">
   content 2
  </div>
  <div class="footer content-visibility">
    footer
   <img loading="lazy" class="content-inner" src="https://via.placeholder.com/301x300">
   <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x300">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x300">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/303x300">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/304x300">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/305x300">
    <div style="height:200px"></div>
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/306x300">
    <img loading="lazy" class="content-inner" src="https://via.placeholder.com/307x300">
  </div>
</div>

如果你快速向下滚动,你会看到日志语句“stick header”,这是错误的,应该只在向上滚动时发生。 现在的问题是,在浏览器呈现内容可见性元素的那一刻,javascript 检测到向上滚动,因为文档高度发生了变化(我猜)。

有什么办法可以防止这种情况发生吗?

【问题讨论】:

    标签: javascript css scroll


    【解决方案1】:

    通过添加contain-intrinsic-size,浏览器可以大致知道元素的大小。它不需要准确,因为浏览器最终会计算实际高度,尽管你越靠近发生跳跃的次数就越少。

    旁注:将widthheight 属性添加到延迟加载的图像会告诉浏览器在加载时保留空间,以减少内容跳转。

    $(document).ready(function(){
      var lastScrollTop = window.pageOffsetY || 0;
      var isFixed = false;
      $(window).on('scroll', _.throttle(function(){
          var scrollTop = $(window).scrollTop();
          if(scrollTop > 200 && scrollTop < lastScrollTop && !isFixed){
            console.log('stick header');
            $('.header').addClass('header-fixed');
            isFixed = true;
          } else if((scrollTop > lastScrollTop && isFixed) || scrollTop == 0){
            console.log('unstick header');
            $('.header').removeClass('header-fixed');
            isFixed = false;
          }
        lastScrollTop = scrollTop;
      }, 500));
    });
    .content {
      height: 200vh;
      background-color: grey;
    }
    
    .content-visibility {
      content-visibility: auto;
      contain-intrinsic-size: 0 5000px; /* width height */
    }
    
    .content-inner {
      height: 100px;
      background-color: green;
    }
    
    img.content-inner {
      height: auto;
    }
    
    .content.content-visibility {
      height: auto;
      background-color: #ddd;
    }
    
    .footer {
      background-color: #eee;
    }
    
    .header {
      height: 200px;
      background-color: white;
    }
    
    
    .header-fixed {
      position: fixed;
      top: 0;
      width: 100vw;
      left: 0;
      z-index: 10;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
    
    <div class="container">
      <div class="header">
        header
      </div>
      <div class="content">
        content 1
      </div>
      <div class="content-visibility">
         <img loading="lazy" class="content-inner" src="https://via.placeholder.com/301x310" width="301" height="310">
       <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x303" width="302" height="303">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x340" width="302" height="340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/303x340" width="303" height="340">
            <div style="height:200px"></div>
    
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/304x340" width="304" height="340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/305x340" width="305" height="340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/306x340" width="306" height="340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/307x340" width="307" height="340">
      </div>
      <div class="content">
       content 2
      </div>
      <div class="footer content-visibility">
        footer
       <img loading="lazy" class="content-inner" src="https://via.placeholder.com/301x300" width="301" height="300">
       <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x300" width="302" height="300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x300" width="302" height="300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/303x300" width="303" height="300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/304x300" width="304" height="300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/305x300" width="305" height="300">
        <div style="height:200px"></div>
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/306x300" width="306" height="300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/307x300" width="307" height="300">
      </div>
    </div>

    【讨论】:

    • 感谢您的回答!不幸的是,我不知道内容会是什么,因为它是由 CMS 设置的不同组件,此处带有图像的部分仅用于说明目的。因此,很难猜测内在大小
    【解决方案2】:

    将事件更改为轮子,一切正常。 滚动的问题是当您的图像加载时,您的 scrollTop 变量也会发生变化(在滚动事件上运行)。 但是 on Wheel 事件仅在您使用轮子时运行。

    $(document).ready(function(){
      var body = document.body,
        html = document.documentElement;
    
      var height = Math.max( body.scrollHeight, body.offsetHeight, 
                           html.clientHeight, html.scrollHeight, html.offsetHeight );
      var lastScrollTop = window.pageOffsetY || 0;
      var isFixed = false;
      $(window).on('wheel touchmove', _.throttle(function(){
          var scrollTop = $(window).scrollTop();
          var newheight = Math.max( body.scrollHeight, body.offsetHeight, 
                           html.clientHeight, html.scrollHeight, html.offsetHeight );
        console.log( height, newheight); 
          if( height !== newheight){
            height = newheight; 
            lastScrollTop = scrollTop;
            return;
          }
          if(scrollTop > 200 && scrollTop < lastScrollTop && !isFixed){
            console.log('stick header');
            $('.header').addClass('header-fixed');
            isFixed = true;
          } else if((scrollTop > lastScrollTop && isFixed) || scrollTop == 0){
            console.log('unstick header');
            $('.header').removeClass('header-fixed');
            isFixed = false;
          }
        lastScrollTop = scrollTop;
      }, 500));
    });
    .content {
      height: 200vh;
      background-color: grey;
    }
    
    .content-visibility {
      content-visibility: auto;
    }
    
    .content-inner {
      height: 100px;
      background-color: green;
    }
    
    img.content-inner {
      height: auto;
    }
    
    .content.content-visibility {
      height: auto;
      background-color: #ddd;
    }
    
    .footer {
      background-color: #eee;
    }
    
    .header {
      height: 200px;
      background-color: white;
    }
    
    
    .header-fixed {
      position: fixed;
      top: 0;
      width: 100vw;
      left: 0;
      z-index: 10;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
    
    <div class="container">
      <div class="header">
        header
      </div>
      <div class="content">
        content 1
      </div>
      <div class="content-visibility">
         <img loading="lazy" class="content-inner" src="https://via.placeholder.com/301x310">
       <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x303">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/303x340">
            <div style="height:200px"></div>
    
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/304x340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/305x340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/306x340">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/307x340">
      </div>
      <div class="content">
       content 2
      </div>
      <div class="footer content-visibility">
        footer
       <img loading="lazy" class="content-inner" src="https://via.placeholder.com/301x300">
       <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/302x300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/303x300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/304x300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/305x300">
        <div style="height:200px"></div>
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/306x300">
        <img loading="lazy" class="content-inner" src="https://via.placeholder.com/307x300">
      </div>
    </div>

    更新: 或者!使用这个新的 sn-p。它检查页面的高度是否发生了变化,如果是,它会更新变量并跳过此事件的发生。

    在滚轮旁边添加 touchmove 以支持触摸(电话)。

    【讨论】:

    • 但是箭头键、锚链接、向下翻页键等呢?我看到了你的方法,但觉得它为潜在的错误开辟了很多空间
    • 是的,没错。更新了我的 sn-p。这应该可以完成工作
    猜你喜欢
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    • 2014-05-09
    • 2016-05-15
    • 2012-06-02
    相关资源
    最近更新 更多