本人最近在修改 blogsue 中的样式时,使用到了 position: sticky
。话不多说,开始主要内容。
定义
position: sticky
是 CSS position
属性的一个新值。正如它的名字那样,它会“黏在”你的浏览器窗口中。这个展示方式有很多的应用场景。例如知乎的右侧就是这样一个场景:当用户一直往下翻的时候右侧的专栏(广告)固定住,不会消失在用户界面。又例如手机端的美团,上面的筛选框也需要保持左边固定。
正如之前的瀑布流与 colum-count
一样,这类应用广泛的排版格式最终都会有原生的实现。 具体使用方式此处就不展开了,可以参照MDN:https://developer.mozilla.org/zh-CN/docs/Web/CSS/position
Polyfill——stickyfill
position: sticky
作为新特性,兼容问题一直是一个迈不过去的坎。可以看到整个 IE 系列都不支持:
position: sticky
的完全实现。**他们的最终效果有些许差异:
- stickyfill 不支持x轴
- stickyfill 会将元素限制在父元素内,即父元素离开屏幕后该元素也会离开(贴着父元素的边)
stickyfill 用法介绍
在 stickyfill repo 中,作者介绍了该 polyfill 的使用方式:
<div class="sticky">
...
</div>
复制代码
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
}
复制代码
Then apply the polyfill:
var elements = document.querySelectorAll('.sticky');
Stickyfill.add(elements);
复制代码
pollyfill 作为“补丁”,最理想的状态下是只需要将其代码引入到项目中,之后不需要做任何事情。例如 Promise 的 polyfill,就是直接在 global 下创建了 promise 类,我们只需引入,其会自动帮我们做好准备工作。但 stickyfill能否这样做呢? 理论上是可以的。因为 stickyfill 只需要遍历 DOM 树找出所有 position
attribute 为 sticky
的 DOM 节点,然后对其添加规则即可。但在实际中,由于遍历 DOM 树性能消耗太高,stickyfill 退而求其次,让我们来选择需要遍历的节点。
源码简析
刚刚我们知道了 stickyfill 的用法,可以知道,stickyfill 是将我们所需要处理的元素进行了托管,利用 javascript 的能力来模拟实现 position: sticky
的功能。 接下来我们一起去看一下 stickyfill 是如何管理、处理元素的。基于文章长度限制,本文只讲解核心的几个方法。下面的源码为了条理清晰,经过精简:
包内预设变量 && 托管元素自定义类
stickyfill 模块内预设了一些类以及变量:
// 此处 stickies 是该库存放所有托管节点的数组
const stickies = [];
// 用来存放最新状态的top和left值
const scroll = {
top: null,
left: null
};
// Sticky类
// 所有确认需要维护的节点都会被这个类wrap
class Sticky {
constructor (node) {
// 差错检测
if (!(node instanceof HTMLElement))
throw new Error('First argument must be HTMLElement');
// 防止重复出现相同的DOM节点
if (stickies.some(sticky => sticky._node === node))
throw new Error('Stickyfill is already applied to this node');
// wrap的DOM节点
this._node = node;
// 存放DOM节点当前的状态,有三个值: