純CSS解決H5布局中的吸頂吸底的實(shí)現(xiàn)步驟
哪些想啥提啥的產(chǎn)品們
最近做了一個(gè)需求,準(zhǔn)確說(shuō)是迭代需求:加了一個(gè)頭部概覽(類似下圖),以更好的讓用戶觀察到營(yíng)銷變化,故事的開(kāi)頭就這樣悄悄的埋下了伏筆。
以前這個(gè)頁(yè)面只是一個(gè)評(píng)價(jià)列表(可上拉加載),為了數(shù)據(jù)更易讀,列表的頭采用了固定布局。然而加了這個(gè)概覽時(shí),產(chǎn)品沒(méi)提,我就簡(jiǎn)單粗暴的將這個(gè)列表頭換成了相對(duì)布局,ok,提測(cè)。

但第二天,我發(fā)現(xiàn)上拉加載數(shù)據(jù)多了,列表頭部被頂上去之后,想再做篩選,就要再把列表上滑才能看到,這個(gè)體驗(yàn)非常之差。于是同事就說(shuō)要不問(wèn)問(wèn)產(chǎn)品,要不把概覽加概覽做成固定。
我第一反應(yīng)就是,恐怕提了之后,產(chǎn)品會(huì)讓我 只 把篩選列表頭部做成固定,注意那個(gè)只。
然后就有了下面的對(duì)話:

果然怕什么,來(lái)什么。但就像同事說(shuō)的,自己?jiǎn)柕男枨?,含著淚也要接下。
后面經(jīng)評(píng)論提點(diǎn),又加入了sticky的方案,確實(shí)是最優(yōu)解。
局部吸頂
以下代碼是頁(yè)面的 dom 結(jié)構(gòu)
<div id="demo" className={style.demo}>
<h3 id="title" className="title">這是一個(gè)概覽頭部</h3>
<div id="content" className="content">
<div className="filter-bar">
<h3>這是列表頭部</h3>
<h3>可篩選</h3>
<h3>下面是滾動(dòng)列表</h3>
</div>
<ul className="list">
{arr.map(({ key, label }) => <li key={key}>{label}</li>)}
</ul>
</div>
</div>
JS 實(shí)現(xiàn)
因?yàn)轫?yè)面本身就有scroll事件監(jiān)聽(tīng),所以第一個(gè)念頭是用JS完成,但當(dāng)時(shí)已經(jīng)下班,又是周五,感覺(jué)5分鐘內(nèi)搞不定,所以我就跑了。
現(xiàn)在來(lái)嘗試用JS實(shí)現(xiàn),先理一下思路:
- 監(jiān)聽(tīng)頁(yè)面的滾動(dòng),當(dāng)ul元素頂部距離頁(yè)面頂部大于title 高度時(shí),添加一個(gè)css類使篩選頭部吸頂;
- 當(dāng)ul元素距離頂部小于等于title 高度時(shí),刪除添加的類,取消篩選頭部吸頂
JS 代碼
useEffect(() => {
const demo = document.querySelector('#demo');
const content = document.querySelector('#content');
const titleHeight = document.querySelector('#title').clientHeight;
let fixed = false;
demo.addEventListener('scroll', (e) => {
// 添加吸頂
if (!fixed && e.target.scrollTop >= titleHeight) {
fixed = true;
content.classList.add('with-fixed');
}
// 取消吸頂
if (fixed && e.target.scrollTop < titleHeight - 5) {
content.classList.remove('with-fixed');
fixed = false;
}
});
}, []);
看起也不難,但其實(shí)離代碼上線,還有很大優(yōu)化的空間,后面會(huì)分析補(bǔ)充。
CSS Viewport實(shí)現(xiàn)
JS 看似很簡(jiǎn)單,但就像那句熱門(mén)句子: 這突如其來(lái)的噩耗,讓本不富裕的家庭雪上加霜 。在這種有下拉加載的頁(yè)面,我們本來(lái)就在監(jiān)聽(tīng)里面做了很多邏輯處理,所以能用CSS實(shí)現(xiàn)的,就盡量不要再去麻煩JS了。
首先理一下思路,深挖產(chǎn)品的需求:
- 保持篩選頭在可視范圍之內(nèi)(吸頂), 保證可篩選;
- 當(dāng)列表數(shù)據(jù)多時(shí),盡可能多展示列表,即概覽頭部就沒(méi)必要看到了;
- 列表是上拉加載的;
當(dāng)理清上面思路時(shí),我們發(fā)現(xiàn),其實(shí)就是當(dāng)列表很長(zhǎng)時(shí),隱藏概覽頭部,簡(jiǎn)單用偽代碼表示就是(vh是視口單位 ,100vh代表整個(gè)屏幕可視高度):
if (titleHeight + filterBarHeight + listHeight > 100vh) {
title.hide();
}
那又怎樣實(shí)現(xiàn)概覽頭部隱藏,而篩選頭和列表又正好出于視口呢?
filterBarHeight + listHeight = 100vh
當(dāng)用戶往上劃,只需要內(nèi)容(篩選頭和列表)正好是一個(gè)視口高度(100vh)時(shí),概覽頭就恰好被隱藏,而篩選頭又正好吸頂,用CSS實(shí)現(xiàn)就是類似這樣的:
// 不是完整代碼,詳情請(qǐng)看demo:
.demo {
:global {
.title {
height: 15vh;
line-height: 15vh;
text-align: center;
border-bottom: 1PX solid #eee;
background-color: #fff;
}
.filter-bar {
height: 15vw;
background-color: #888;
display: flex;
align-items: center;
}
.list {
max-height: calc(100vh - 15vw);; // 這里的設(shè)置很重要
overflow: scroll;
background-color: rgba(127, 255, 212, .8);
}
最優(yōu)實(shí)現(xiàn) CSS Sticky
在css中的定位( position )屬性值中有個(gè)不常用的: sticky ;
元素根據(jù)正常文檔流進(jìn)行定位,然后相對(duì)它的最近滾動(dòng)祖先和最近塊級(jí)祖先,包括table-related元素,基于top, right, bottom, 和 left的值進(jìn)行偏移。粘性定位可以被認(rèn)為是相對(duì)定位和固定定位的混合。元素在跨越特定閾值前為相對(duì)定位,之后為固定定位。
這里我們?cè)谘赜肑S的方案上進(jìn)行更改,只需要將filter-bar 的定位屬性改為粘性定位,就可以去除對(duì) JS 的依賴;
.demo {
max-height: 100%;
overflow: scroll;
.filter-bar {
position: sticky;
top: 0;
}
}
demo類作用于最外層dom( <div className={style.demo}> )上,其視高為100vh,當(dāng)內(nèi)容超出高度時(shí)為滾動(dòng);filter-bar 元素采用粘性定位,當(dāng)高度距離demo元素大于0時(shí),其采用相對(duì)定位,即以正常文檔流的形式定位;當(dāng)高度小于等于0時(shí),其采用固定定位,就達(dá)到吸頂?shù)男Ч?/p>
對(duì)比
是不是感覺(jué)CSS很簡(jiǎn)單,稍微設(shè)置一下即搞定,只是要想到內(nèi)容高度正好是 100vh 需要一點(diǎn)經(jīng)(yun)驗(yàn)(qi),經(jīng)常寫(xiě)H5的,sticky的方案相信也是新手黏來(lái)。其實(shí)不光簡(jiǎn)便,對(duì)比JS至少還有兩個(gè)個(gè)優(yōu)點(diǎn):
- JS 如果只是上面那樣,直接將篩選頭的定位改成固定定位,眼力好的人,其實(shí)是能感覺(jué)到列表有跳變的一瞬間,就是列表會(huì)突然上移filterBar高度,來(lái)填補(bǔ)篩選頭離開(kāi)正常文檔流;(解決方案就是在篩選頭外多套一層dom,并給一個(gè)固定高度,這樣篩選頭脫離正常文檔流,但高度依舊還在);
- 當(dāng)用JS來(lái)操作Dom元素重排時(shí),這每年面試官說(shuō)的那些重繪重拍我就不多說(shuō)了,這消耗的性能肯定高于CSS實(shí)現(xiàn);
當(dāng)然,viewport方案還有個(gè)ios手機(jī)的兼容性問(wèn)題,由于safari的頭部和底部滑動(dòng)時(shí)可見(jiàn)性會(huì)改變,所以當(dāng)Bar可見(jiàn)時(shí),實(shí)際的100vh高于屏幕可見(jiàn)高度,就會(huì)導(dǎo)致吸頂頭部被遮擋。到目前為止,雖然網(wǎng)上有很多說(shuō) height: -webkit-fill-available; ,但針對(duì)這種場(chǎng)景是無(wú)效的;但只要依賴100vh,都面臨這種困局,safari太奇葩,下一個(gè)IE就是它了.

經(jīng)過(guò)上面分析,100vh在IOS safari上的致命問(wèn)題,會(huì)讓這種100vh這種純CSS的方案褪色。但PC頁(yè)面,或者你和我一樣,要編寫(xiě)的頁(yè)面是運(yùn)行在APP中(即沒(méi)有bar存在),那這種方案就是可行的。所有的方案都要具體場(chǎng)景,具體分析,沒(méi)有誰(shuí)出生就是完美。這里只是提高一種思考方式,長(zhǎng)點(diǎn)見(jiàn)識(shí)。
而sticky方案不依賴于100vh,其可以用100%的寫(xiě)法,所以沒(méi)有這個(gè)擔(dān)憂,所以相比之下,最優(yōu)解就是 sticky ; 但 height: 100% 是個(gè)無(wú)底洞,你需要從html 標(biāo)簽寫(xiě)起,一直寫(xiě)到具有滾動(dòng)的容器元素。
彈性吸底
說(shuō)完局部彈性吸頂,再說(shuō)一個(gè)常見(jiàn)的,選擇性吸底:在頁(yè)面內(nèi)容不足100vh時(shí),我們希望 Footer 是吸底的,當(dāng)頁(yè)面內(nèi)容大于100vh時(shí), Footer 處于正常文檔流,讓內(nèi)容可視區(qū)域更大,而又不會(huì)因?yàn)閮?nèi)容太少影響美觀,見(jiàn)圖:

像第一張圖那樣不做定位的還是大有人在,因?yàn)樗麄儓?jiān)信自己網(wǎng)站的內(nèi)容不會(huì)出現(xiàn)不夠的時(shí)候,但以前更常見(jiàn)做法是底部固定定位。
彈性吸底利用 min-height 加絕對(duì)定位,其實(shí)現(xiàn)很簡(jiǎn)單。核心代碼不超過(guò)5行css:
body{
position: relative;
min-height: 100vh;
}
footer {
width: 100%;
position: absolute;
bottom: 0;
}
原理就是內(nèi)容區(qū)域最低高度為一個(gè)屏幕,然后底部相對(duì)屏幕進(jìn)行絕對(duì)定位;當(dāng)內(nèi)容變多時(shí),高度大于100vh,由于是依賴 bottom: 0; ,所以會(huì)一直吸底,其巧妙之處就在于此。
針對(duì)于這個(gè)場(chǎng)景, height: -webkit-fill-available 就是有效的。

到此這篇關(guān)于純CSS解決H5布局中的吸頂吸底的實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)CSS 吸頂吸底內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章

Html5導(dǎo)航欄吸頂方案原理與對(duì)比實(shí)現(xiàn)
這篇文章主要介紹了Html5導(dǎo)航欄吸頂方案原理與對(duì)比實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起2020-06-10
css實(shí)現(xiàn)5種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較(性能升級(jí)版)
這篇文章主要介紹了css實(shí)現(xiàn)5種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較(性能升級(jí)版),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著2019-07-31
吸頂是一種比較常見(jiàn)的交互效果,當(dāng)頁(yè)面滑出屏幕邊界,標(biāo)題會(huì)自動(dòng)吸附在屏幕邊緣,用于提示用戶,這篇文章主要介紹了Html5之title吸頂功能的相關(guān)資料,感興趣的小伙伴們可以2018-06-04




