监听 scroll 计算

临界点:

image.png

向上滚动页面,粘性元素逐渐靠近浏览器顶部区域,这个过程中始终 container.offsetTop < el.offsetTop ,还没达到吸顶条件:

image.png

触顶时就得吸顶了,此刻 container.offsetTop == el.offsetTop

image.png

直到 container.offsetTop < el.offsetTop + el.clientHeight 之前,都需要持续吸顶:

image.png

一旦 container.offsetTop > el.offsetTop + el.clientHeight ,取消吸顶:

image.png

代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .container {
        width: 100vw;
        height: 100vh;
        box-sizing: border-box;
        padding: 48px 0 50px;
      }
      .header {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 48px;
        background-color: pink;
      }
      .footer {
        position: fixed;
        width: 100%;
        height: 50px;
        left: 0;
        bottom: 0;
        background-color: pink;
      }
      .content {
        position: relative;
        height: 100%;
        overflow-y: scroll;
        box-sizing: border-box;
      }
      /* section h2 {
        position: sticky;
        top: 0;
        background-color: lightyellow;
      } */
      section {
        position: relative;
        box-sizing: border-box;
        padding-top: 40px;
      }
      section h2 {
        display: flex;
        justify-content: center;
        align-items: center;
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 40px;
        background-color: lightyellow;
      }
      section h2.sticky {
        position: fixed;
        top: 48px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="header"></div>
      <div class="content">
        <section>
          <h2>HEADER1</h2>
          <div class="desc">
            ...
          </div>
        </section>
        <section>
          <h2>HEADER2</h2>
          <div class="desc">
            ...
          </div>
        </section>
        <section>
          <h2>HEADER3</h2>
          <div class="desc">
            ...
          </div>
        </section>
      </div>
      <div class="footer"></div>
    </div>
    <script>
      const oContent = document.getElementsByClassName("content")[0];
      const oH2List = document.getElementsByTagName("h2");
      const offsetTopList = [];
      [...oH2List].forEach((ele) => {
        offsetTopList.push({
          offsetTop: ele.parentElement.offsetTop,
          clientHeight: ele.parentElement.clientHeight,
        });
      });
      oContent.onscroll = function (e) {
        offsetTopList.forEach((v, idx) => {
          if (
            oContent.scrollTop > v.offsetTop &&
            oContent.scrollTop < v.clientHeight + v.offsetTop
          ) {
            oH2List[idx].classList.add("sticky");
          } else {
            oH2List[idx].classList.remove("sticky");
          }
        });
      };
    </script>
  </body>
</html>

示例

https://codepen.io/JingW/pen/xxvpLBg

粘性布局 sticky

position 中的 sticky 值是 CSS3 新增的,设置了 sticky 值后,在屏幕范围(viewport)时该元素的位置并不受到定位影响(设置是 topleft 等属性无效),当该元素的位置将要移出偏移范围时,定位又会变成 fixed,根据设置的 lefttop 等属性成固定位置的效果。

sticky 属性值有以下几个特点: