演示Demo地址(手機端打開):https://closertb.site/Klotski...
演示Demo源碼:https://github.com/closertb/k...
原文:https://github.com/closertb/c...css
最近作了一個需求,準確說是迭代需求:加了一個頭部概覽(相似下圖),以更好的讓用戶觀察到營銷變化,故事的開頭就這樣悄悄的埋下了伏筆。html
之前這個頁面只是一個評價列表(可上拉加載),爲了數據更易讀,列表的頭採用了固定佈局。然而加了這個概覽時,產品沒提,我就簡單粗暴的將這個列表頭換成了相對佈局,ok,提測。
但次日,我發現上拉加載數據多了,列表頭部被頂上去以後,想再作篩選,就要再把列表上滑才能看到,這個體驗很是之差。因而同事就說要不問問產品,要不把概覽加概覽作成固定。前端
我第一反應就是,恐怕提了以後,產品會讓我只
把篩選列表頭部作成固定,注意那個只。ios
而後就有了下面的對話:git
果真怕什麼,來什麼,畢竟是很常規的操做。但就像同事說的,本身問的需求,含着淚也要接下。github
如下代碼是頁面的dom
結構web
<div id="demo" className={style.demo}> <h3 id="title" className="title">這是一個概覽頭部</h3> <div id="content" className="content"> <div className="filter-bar"> <h3>這是列表頭部</h3> <h3>可篩選</h3> <h3>下面是滾動列表</h3> </div> <ul className="list"> {arr.map(({ key, label }) => <li key={key}>{label}</li>)} </ul> </div> </div>
由於頁面自己就有scroll事件監聽,因此第一個念頭是用JS完成,但當時已經下班,又是週五,感受5分鐘內搞不定,因此我就跑了。面試
如今來嘗試用JS實現,先理一下思路:瀏覽器
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; } }); }, []);
看起也不難,但其實離代碼上線,還有很大優化的空間,後面會分析補充。less
JS 看似很簡單,但就像那句熱門句子:這突如其來的噩耗,讓本不富裕的家庭雪上加霜
。在這種有下拉加載的頁面,咱們原本就在監聽裏面作了不少邏輯處理,因此能用CSS實現的,就儘可能不要再去麻煩JS了。
首先理一下思路,深挖產品的需求:
當理清上面思路時,咱們發現,其實就是當列表很長時,隱藏概覽頭部,簡單用僞代碼表示就是(vh是視口單位 ,100vh表明整個屏幕可視高度):
if (titleHeight + filterBarHeight + listHeight > 100vh) { title.hide(); }
那又怎樣實現概覽頭部隱藏,而篩選頭和列表又正好出於視口呢?
filterBarHeight + listHeight = 100vh
當用戶往上劃,只須要內容(篩選頭和列表)正好是一個視口高度(100vh)時,概覽頭就剛好被隱藏,而篩選頭又正好吸頂,用CSS實現就是相似這樣的:
// 不是完整代碼,詳情請看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);; // 這裏的設置很重要 overflow: scroll; background-color: rgba(127, 255, 212, .8); }
### 對比
是否是感受CSS很簡單,稍微設置一下即搞定,只是要想到內容高度正好是100vh
須要一點經(yun)驗(qi)。其實不光簡便,對比JS至少還有三個優勢:
filterBar
高度,來填補篩選頭離開正常文檔流;(解決方案就是在篩選頭外多套一層dom,並給一個固定高度,這樣篩選頭脫離正常文檔流,但高度依舊還在);固然缺點也是存在的:
height: -webkit-fill-available;
,但針對這種場景是無效的;
通過上面分析,100vh在IOS safari上的致命問題,會讓這種純CSS的方案褪色。但PC頁面,或者你和我同樣,要編寫的頁面是運行在APP中(即沒有bar存在),那這種方案就是可行的。全部的方案都要具體場景,具體分析,沒有誰出生就是完美。
若是對重繪重排有興趣,建議觀看Chrome的官方博文: 瀏覽器四部曲
說完局部彈性吸頂,再說一個常見的,選擇性吸底:在頁面內容不足100vh時,咱們但願Footer
是吸底的,當頁面內容大於100vh時,Footer
處於正常文檔流,讓內容可視區域更大,而又不會由於內容太少影響美觀,見圖:
像第一張圖那樣不作定位的仍是大有人在,由於他們堅信本身網站的內容不會出現不夠的時候,但之前更常見作法是底部固定定位。
彈性吸底利用min-height
加絕對定位,其實現很簡單。核心代碼不超過5行css:
body{ position: relative; min-height: 100vh; } footer { width: 100%; position: absolute; bottom: 0; }
原理就是內容區域最低高度爲一個屏幕,而後底部相對屏幕進行絕對定位;當內容變多時,高度大於100vh,因爲是依賴bottom: 0;
,因此會一直吸底,其巧妙之處就在於此。
針對於這個場景,height: -webkit-fill-available
就是有效的。
更多關於-webkit-fill-available, 參見[https://allthingssmitty.com/2...];
vh 確實是個好東西,能夠解決移動端的適配問題。我我的以爲做爲一個合格的前端,CSS 仍然是必備技能,不要對JS產生太多的依賴,不是不能夠,而是好鋼要用在刀刃上。