Scroll ceiling effect - four ways to achieve

Scroll ceiling effect. I was very puzzled why there will be a rolling ceiling bug, but then I look at the code found directly using a offsetTop this property, but did not do a compatibility processing.

offsetTop

Current element for obtaining a distance (offset) is positioned on top of the parent (element.offsetParent).

Custom targeting parent offsetParent is:! Nearest position = current element of the parent element static.

Perhaps writing this code, people do not notice that this subsidiary condition "locate the parent."
Later in the project will always encounter the need to achieve the effect of rolling ceiling, now I know I will be four types of rolling Ceiling implementation detailed description.

table of Contents

  1. Use position: sticky achieve
  2. JQuery use of offset (). Top realization
  3. Use offsetTop achieve native
  4. Use obj.getBoundingClientRect (). Top realization

Hope to support what a star.

Four kinds of implementations

We look at renderings:


First, the use position: sticky achieve

1. What viscous positioning?

When the page elements during scrolling, a distance from an element of the parent element positioned to meet the requirements sticky viscous;; sticky viscous positioned corresponding to the relative positioning and relative binding fixedly positioned relative effect fixed the relative positioning of the positioning element is fixed into a fixed Effect.

MDN Portal

2, How to use?

Conditions of Use:

Parent element can not overflow: hidden or overflow: auto attribute
must specify the top, bottom, left, right one of four values, otherwise it will be in the relative positioning
of the parent element a height of not less than sticky elements of highly
sticky element only in the parent element take effect within

The need to scroll ceiling elements plus the following styles will be able to achieve this effect:

?
1
2
3
4
5
.sticky {
position : -webkit-sticky;
position : sticky;
top : 0 ;
}

3, this property is easy to use it?

We look at look at this property in Can I use Compatibility:

This property can be seen that compatibility is not very good, because the API is only property experimental. But this API compatibility IOS system is still relatively good.
So we usually following several ways and when used in conjunction with the production environment if you use this API.

Second, the use of JQuery offset (). Top realization

We know JQuery DOM and encapsulates the read operation of calculating an attribute DOM API, based on the offset (). This API and scrollTop Top () binding, we can achieve a rolling ceiling effect.

?
1
2
3
4
5
6
7
8
9
10
...
window.addEventListener( 'scroll' , self.handleScrollOne);
...
handleScrollOne: function () {
let self = this ;
let scrollTop = $( 'html' ).scrollTop();
let offsetTop = $( '.title_box' ).offset().top;
self.titleFixed = scrollTop > offsetTop;
}
...

Of course this can be achieved, but because JQuery slowly exit the stage of history, we try not to use the API JQuery in the code. We can offset based on (). Top source deal with their own native offsetTop. Ever since there is a third way.

scrolloTop () compatibility issues, micro-channel in the browser, IE, firefox some versions ( 'html'). scrollTop () $ The value is 0, and thus will have a compatibility with the wording of the third scheme .

Third, the use offsetTop achieve native

We know offsetTop is the parent of the relative positioning of the offset, if the need to scroll ceiling elements appear positioned parent element, from the top so it is not element offsetTop acquired from the page.

We can do it yourself the following processing offsetTop:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
getOffset: function (obj,direction){
let offsetL = 0;
let offsetT = 0;
while ( obj!== window.document.body && obj !== null ){
offsetL += obj.offsetLeft;
offsetT += obj.offsetTop;
obj = obj.offsetParent;
}
if (direction === 'left' ){
return offsetL;
} else {
return offsetT;
}
}

use:

?
1
2
3
4
5
6
7
8
9
10
...
window.addEventListener( 'scroll' , self.handleScrollTwo);
...
handleScrollTwo: function () {
let self = this ;
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
let offsetTop = self.getOffset(self.$refs.pride_tab_fixed);
self.titleFixed = scrollTop > offsetTop;
}
...

Do you see some problems over the two approaches?

We certainly need to use scrollTop - offsetTop value to achieve a rolling ceiling effect? the answer is negative.

Together we look at the fourth program.

Fourth, the use obj.getBoundingClientRect (). Top realization

Definition:
This API can tell you that an element in the page relative distance down about the window view.
Use:
Tab ceiling can obj.getBoundingClientRect () top in place scrollTop - offsetTop, code is as follows:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// html
<div class= "pride_tab_fixed" ref= "pride_tab_fixed" >
<div class= "pride_tab" :class= "titleFixed == true ? 'isFixed' :''" >
// some code
</div>
</div>
// vue
export default {
data(){
return {
titleFixed: false
}
},
activated(){
this .titleFixed = false ;
window.addEventListener( 'scroll' , this .handleScroll);
},
methods: {
//滚动监听,头部固定
handleScroll: function () {
let offsetTop = this .$refs.pride_tab_fixed.getBoundingClientRect().top;
this .titleFixed = offsetTop < 0;
// some code
}
}
}

offsetTop 和 getBoundingClientRect() 区别

1. getBoundingClientRect():

用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。不包含文档卷起来的部分。
该函数返回一个 object 对象,有6个属性:
top, right, buttom, left, width, height。
(在 IE 中,默认坐标从(2,2)开始计算,只返回 top,lef,right,bottom 四个值)

2. offsetTop:

用于获得当前元素到定位父级( element.offsetParent )顶部的距离(偏移值)。

定位父级 offsetParent 的定义是:与当前元素最近的 position != static 的父级元素。

offsetTop 和 offsetParent 方法相结合可以获得该元素到 body 上边距的距离。代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
getOffset: function (obj,direction){
let offsetL = 0;
let offsetT = 0;
while ( obj!== window.document.body && obj !== null ){
offsetL += obj.offsetLeft;
offsetT += obj.offsetTop;
obj = obj.offsetParent;
}
if (direction === 'left' ){
return offsetL;
} else {
return offsetT;
}
}

延伸知识点

offsetWidth:

元素在水平方向上占用的空间大小:
offsetWidth = border-left + padding-left + width + padding-right + border-right

offsetHeight:

元素在垂直方向上占用的空间大小:
offsetHeight = border-top + padding-top + height + padding-bottom + border-bottom

注:如果存在垂直滚动条,offsetWidth 也包括垂直滚动条的宽度;如果存在水平滚动条,offsetHeight 也包括水平滚动条的高度;

offsetTop:

元素的上外边框至 offsetParent 元素的上内边框之间的像素距离;

offsetLeft:

元素的左外边框至 offsetParent 元素的左内边框之间的像素距离;

注意事项

  1. 所有偏移量属性都是只读的;
  2. 如果给元素设置了 display:none,则它的偏移量属性都为 0;
  3. 每次访问偏移量属性都需要重新计算(保存变量);
  4. 在使用的时候可能出现 DOM 没有初始化,就读取了该属性,这个时候会返回 0;对于这个问题我们需要等到 DOM 元素初始化完成后再执行。

遇到的两个问题

一、吸顶的那一刻伴随抖动

出现抖动的原因是因为:在吸顶元素 position 变为 fixed 的时候,该元素就脱离了文档流,下一个元素就进行了补位。就是这个补位操作造成了抖动。

解决方案
为这个吸顶元素添加一个等高的父元素,我们监听这个父元素的 getBoundingClientRect().top 值来实现吸顶效果,即:

?
1
2
3
4
5
<div class= "title_box" ref= "pride_tab_fixed" >
<div class= "title" :class= "titleFixed == true ? 'isFixed' :''" >
使用 `obj.getBoundingClientRect().top` 实现
</div>
</div>

这个方案就可以解决抖动的 Bug 了。

二、吸顶效果不能及时响应

这个问题是我比较头痛,之前我没有在意过这个问题。直到有一天我用美团点外卖的时候,我才开始注意这个问题。
描述:

当页面往下滚动时,吸顶元素需要等页面滚动停止之后才会出现吸顶效果
当页面往上滚动时,滚动到吸顶元素恢复文档流位置时吸顶元素不恢复原样,而等页面停止滚动之后才会恢复原样

原因:

在 ios 系统上不能实时监听 scroll 滚动监听事件,在滚动停止时才触发其相关的事件。
解决方案:
还记得第一种方案中的 position:sticky 吗?这个属性在 IOS6 以上的系统中有良好的兼容性,所以我们可以区分 IOS 和 Android 设备做两种处理。

IOS 使用 position:sticky,Android 使用滚动监听 getBoundingClientRect().top 的值。

如果 IOS 版本过低呢?这里提供一种思路:window.requestAnimationFrame()。


转载于:https://juejin.im/post/5d0858c1f265da1bc14b2a32

Guess you like

Origin blog.csdn.net/weixin_34246551/article/details/93168816