一、場景
需求: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<prefixTestList.length;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:
<body>
????<div class="banner"></div>
????<div class="tab sticky"></div>
????<div?class="content"></div>
</body>
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;
}