一、场景
需求:tab需要在划出视口的时候吸顶(sticky),方便点击切换下方内容。
二、方案
1、采用scroll监听+fixed定位。a) 监听页面被卷高度scrollTop,和 要sticky的元素距离页面的高度offsetTop,如果前者大于后者,设置fixed样式。b)通过getBoundingClientRect()监听要sticky元素的位置top 和0,stickyHeight和bottom进行比较,如果前者小于后者,设置fixed样式。
2、采用position:sticky定位。
三、position : sticky
粘性定位是相对定位和固定定位的混合。元素在跨越特定阈值例如,从视口顶部10像素)前为相对定位,之后为固定定位。
须指定top,right,bottom或left四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。
四、方案实现
.fixed-top{
position: fixed;
left:0;
top:0; //num
z-index:10;
}
1-a、scroll + scrollTop + offsetTop + fixed
$(window).scroll(function(){
var $stickyEl = $('.sticky');
var scrollT = $(window).scrollTop();
if (scrollT > (_stickyT-num)) { //num是吸顶时距离viewport顶部的距离
$stickyEl.addClass('fixed-top');
} else {
$stickyEl.removeClass('fixed-top');
}
});
1-b、scroll + getBoundingClientRect + fixed
$(window).scroll(function() {
var $stickyEl = $('.sticky');
var rect = $stickyEl.getBoundingClientRect();
if(rect.top < 0 && (rect.bottom - stickyHeight) > 0) {
!$stickyEl.hasClass('fixed-top') && $stickyEl.addClass('fixed-top').css('width', stickyWidth +'px');
}else{
$stickyEl.hasClass('fixed-top') && $stickyEl.removeClass('fixed-top').css('width','auto');
}
});
或者
vardocClientWidth = document.documentElement.clientHeight;
rect.bottom > docClientWidth && (rect.top + stickyHeight) < docClientWidth;
2、sticky样式
.sticky-top{
position: -webkit-sticky;
position: sticky;
top:0;
left:0;
z-index:10;
}
五、整体实现
思路:
1)通过sticky属性值是否可用的监测,如果可以,采用方案2,如果不可以,采用方案1-a;
2)在使用方案1-a时,滚动实时监听会带来频繁的调用回调函数,引来性能问题,在此采用函数截流的方式解决;
3)在接触到阀值临界是会有跳动,这是因为当sticky元素被固定的时候,它会脱离普通文档流,所以要利用它的父元素把sticky元素的高度在普通文档流中撑起来,以免在固定效果出现的时候,target元素的内容出现跳动的情况。
ps:(1)一般还是采用scroll,但是ios只有在滚动停止时才会调用回调函数;所以找到了sticky;但是sticky对低版本兼容度不够,但是对目前主流及高版本android和ios兼容度还ok,所以采用sticky优先的原则,判断是否支持sticky,来实现。
整体实现:
js:
//吸顶start
varfixTop={
stickyEl :$('.sticky'),
stickyT:function() {
returnthis.stickyEl.offset().top;
},
num :0,
// bannerImg: document.getElementById("bannerImg"),
init:function(){
// var u = navigator.userAgent;
// var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
// var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
var_num=this.num;
varisSupportSticky=this.isSupportSticky();
if(isSupportSticky) {
this.sticky();
return;
}
this.scroll(_num);
},
scroll:function(num){//滚动监听添加fixed样式
var_this=this;
var_stickyT=_this.stickyT();
this.stickyHolder();
$(window).scroll(this.throttle(function(){
// var scrollT = document.body.scrollTop;
varscrollT=$(window).scrollTop();
// console.log(scrollT,_stickyT-num);
if(scrollT>(_stickyT-num)) {
_this.stickyEl.addClass('fixed-top');
}else{
_this.stickyEl.removeClass('fixed-top');
}
},10));
},
sticky:function(){//添加sticky样式
var_this=this;
_this.stickyEl.addClass('sticky-top');
},
isSupportSticky:function(){//判断是否支持sticky
varprefixTestList=['','-webkit-'];
varstickyText='';
for(vari=0;i stickyText+='position:'+prefixTestList[i]+'sticky;'; } // 创建一个dom来检查 vardiv=document.createElement('div'); div.style.cssText=stickyText; document.body.appendChild(div); varisSupport=/sticky/i.test(window.getComputedStyle(div).position); document.body.removeChild(div); div=null; returnisSupport; }, stickyHolder:function(){//守家占位符 varstickyHolder=document.createElement('div'); varstickyElDom=this.stickyEl.get(0); varrect=stickyElDom.getBoundingClientRect(); // console.log(rect); stickyElDom.parentNode.replaceChild(stickyHolder,stickyElDom); stickyHolder.appendChild(stickyElDom); stickyHolder.style.height=rect.height+'px'; }, throttle:function(func, wait){//函数截流 vartimer=null; returnfunction() { varself=this, args=arguments; if(timer)clearTimeout(timer); timer=setTimeout(function() { returntypeoffunc==='function'&&func.apply(self,args); },wait); } } } fixTop.init(); //吸顶end html:
css:
body{
margin:0;
}
div{
width:100%;
}
.banner{
/* overflow: hidden; */
height:500px;
background-color: yellowgreen;
}
.tab{
height:80px;
background-color: blue;
}
.content{
height:3000px;
}
.fixed-top{
position: fixed;
/*width: 100%;*/
left:0;
top:0;
z-index:10;
}
.sticky-top{
/* 滚过初始位置时自动吸顶 */
/* */
position: -webkit-sticky;
position: sticky;
top:0;
left:0;
z-index:10;
}