sticky还把footer的内容给盖住了,本来是为了方便用户操作,结果影响了用户操作,所以得把取消固定这个临界点提前,而用sticky元素的高度最合适。
通过前面对demo变化过程的拆解,我们已经得到了滚动条一直向下滚动时,sticky状态变化的两个临界点:
1)当target元素的顶部离浏览器顶部的距离小于0的时候,sticky元素就会被固定;
2)当target元素的底部离浏览器顶部的距离小于sticky元素的高度时,sticky元素就会被取消固定。
综合这两个临界点,可以得出滚动条向下滚动时,sticky元素被固定的滚动范围的判断条件是:target元素的顶部离浏览器顶部的距离小于0 并且 target元素的底部离浏览器顶部的距离大于sticky元素的高度。而且这个判断条件,同样适用于滚动条向上滚动的情况,因为滚动条一直向上滚动时,sticky状态变化的临界点是:
1)当target元素的底部离浏览器顶部的距离大于sticky元素的高度时,sticky元素就会被固定;
2)当target元素的顶部离浏览器顶部的距离大于0的时候,sticky元素就会被取消固定。
(这两个临界点,其实跟滚动条向下滚动时提到的两个临界点,是一个意思,只不过是正话反着说而已)
所以只要得到【target元素的顶部离浏览器顶部的距离】,【target元素的底部离浏览器顶部的距离】,【sticky元素的高度】这三个值基本上就能实现这个组件了。这三个值中sticky元素的高度由设计图决定,它从网页一开始制作就是已知的,在定义组件的时候我们可以从外部传进去,虽然也能从js去获取它的高度,不过显然没有必要增加额外的计算;另外两个值【target元素的顶部离浏览器顶部的距离】,【target元素的底部离浏览器顶部的距离】,我们正好可以利用DOM提供的一个方法来获取,这个方法是:getBoundingClientRect,这是一个兼容性很好的方法,它的调用方式是:
var target = document.getElementById('main-container');
var rect = target.getBoundingClientRect();
console.log(rect);
返回一个ClientRect对象,这个对象存储元素框模型的一些信息,比如它的宽高度(width and height),以及元素框上下边距离浏览器顶部边缘的距离(top and bottom),左右边距离浏览器左边缘的距离(left and right):
top跟bottom恰恰就是我们要获取的【target元素的顶部离浏览器顶部的距离】,【target元素的底部离浏览器顶部的距离】,而且当框的顶部或底部未超出浏览器顶部的时候,top跟bottom都是大于0的值,而当框的顶部或底部超出浏览器顶部的时候,top跟bottom是小于0的值:
当我们找到了【target元素的顶部离浏览器顶部的距离】,【target元素的底部离浏览器顶部的距离】,【sticky元素的高度】这三个值,就可以用代码来描述前面的判断条件:
rect.top < 0 && (rect.bottom - stickyHeight) > 0;
(rect表示target元素调用getBoundingClientRect返回的对象,stickyHeight表示sticky元素的高度)
最后为了让实现思路更加完整,虽然不详细介绍固定在底部的情况的变化过程,我还是把这种情况的临界点跟判断方式补充进来,它的临界点是(这里列的是滚动条向下滚动时的临界点):
1)当target元素的顶部离浏览器顶部的距离 + sticky元素的高度 小于浏览器可视区域的高度时,sticky元素被固定;
2)当target元素的底部离浏览器的顶部的距离小于浏览器可视区域的高度时,sticky元素被取消固定。
浏览器可视区域的高度,可用document.documentElement.clientHeight来获取,这个属性也是没有兼容性问题的,判断代码为:
var docClientWidth = document.documentElement.clientHeight; rect.bottom > docClientWidth && (rect.top + stickyHeight) < docClientWidth;
2. 实现细节
1)html结构
固定在顶部的html结构: