CSS Position Sticky
 · 2 min read
CSS属性 position: sticky 可以轻松实现元素滚动置顶显示, 有时候业务上, 元素置顶之后要和非置顶时样式不一样, 但是CSS并没有提供置顶的伪类(::stuck), 网上有一些解决方案结合JS实现, 下面是封装后的JS代码:
sticky.js
/**
 * 置顶元素
 * @param {HTMLElement} ele 需要置顶的元素
 * @param { { onStickyChange: (data: { sticky: boolean, top: number }) => void }} opts 置顶状态发生变化时的回调函数
 */
function stickyElement(ele, opts) {
  const observer = new IntersectionObserver(
    ([e]) => {
      if (!ele.offsetParent) {
        console.warn(`Can't find offsetParent by `, ele);
        return;
      }
      const topDistance = ele.offsetParent.offsetTop;
      const selfTop = ele.getBoundingClientRect().top;
      const isTop = selfTop <= topDistance;
      const stickied = isTop && e.intersectionRatio < 1;
      const data = {
        sticky: stickied,
        top: topDistance,
        element: ele,
      };
      e.target.dispatchEvent(new CustomEvent("sticky-change", {
        detail: data
      }));
      if (opts && opts.onStickyChange) {
        opts.onStickyChange(data);
      }
    },
    { threshold: 1 }
  );
  observer.observe(ele);
  ele.style.position = "sticky";
  ele.style.top = "-2px";
  return () => {
    observer.unobserve(ele);
  }
}
/**
 * 二次封装的置顶函数
 * @param {{ stickyElement: HTMLElement, fixedElement: HTMLElement }} opts 
 */
function stickyThenFixed(opts) {
  stickyElement(opts.stickyElement, {
    onStickyChange: data => {
      const { sticky, top } = data;
      if (sticky) {
        opts.fixedElement.classList.add("fixed");
        opts.fixedElement.style.top = top + "px";
      } else {
        opts.fixedElement.classList.remove("fixed");
        opts.fixedElement.style.top = "0px";
      }
    }
  });
}
使用方法如下:
 stickyElement(document.querySelector("#sticky2"), {
  onStickyChange: data => {
    if (data.sticky) {
      // 元素被置顶了
      data.element.style.background = "white";
      data.element.style.boxShadow = "3px 3px 5px #aaa";
      data.element.style.zIndex = "99";
    } else {
      // 元素取消置顶了
      data.element.style.background = "none";
      data.element.style.boxShadow = "none";
      data.element.style.zIndex = "auto";
    }
  }
});
实际效果参考: 示例页面