Skip to main content

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";
}
}
});

实际效果参考: 示例页面