摘要:今天,你的瀏覽器滾動了嗎序在頁面中,一個有高度或者寬度的容器是最常見的構(gòu)成元素,而在其中的子元素有很大的概率超過父容器的尺寸限制,我們稱之為溢出。
今天,你的瀏覽器 “滾動” 了嗎? 序
在 Web 頁面中,一個有高度或者寬度的容器是最常見的構(gòu)成元素,而在其中的子元素有很大的概率超過父容器的尺寸限制,我們稱之為“溢出”。而應(yīng)對“溢出”,隱藏或者滾動是最常見的處理方式。滾動,作為 FEers 最經(jīng)常處理的一種行為,卻因?yàn)椴煌瑸g覽器的各種表現(xiàn)形式讓大家頭痛不已,今天筆者從自身維護(hù)的組件出發(fā),和大家分享一下自己在處理滾動和滾動條時遇到的問題,以及解決的辦法,希望能夠給你在解決同類問題時帶來一些啟發(fā)。同時本文也是 “從零開始的 React 組件開發(fā)之路” 系列的第二篇 - 表格篇番外。
遇到的問題 1:尷尬的雙滾動條筆者在團(tuán)隊(duì)中負(fù)責(zé)基礎(chǔ)組件的開發(fā)和維護(hù),作為一個 B 類業(yè)務(wù)較多的團(tuán)隊(duì),表格是最常用和需求最為旺盛的組件,假設(shè)有下方這樣一個最簡單的表格結(jié)構(gòu)。
圖1:最簡單的表格結(jié)構(gòu)
因?yàn)榭臻g有限,我們希望表格高度限定,這樣勢必引入表格上下滾動的情況。同時,為了查看的方便,我們希望表格頭不會一起滾動,即表格頭需要固定,只有表格體滾動,因此我們需要把表格頭和表格體放入兩個容器中,而只讓表格體的容器滾動。這是很普通的需求,也很容易實(shí)現(xiàn),到目前為止一切都很順利。
圖2:表頭固定,表格體滾動
然而這一切的美好,隨著表格列數(shù)的增多,變的有了一點(diǎn)烏云。因?yàn)轫撁鎸挾仁艿诫娔X屏幕的限制,我們往往對表格的寬度也有限制,不可能無限延展開。那么如果有很多的列呢?顯然,讓表格左右滾動是一個很自然的想法。由于我們的表頭和表格體在兩個不同的容器中,讓這件事變的稍微麻煩一點(diǎn)。關(guān)于如何讓表頭和表格體同步左右滾動,不是這篇文章討論的重點(diǎn),所以不做詳細(xì)討論,簡單來說,我們通過監(jiān)聽橫向滾動的事件和不斷獲取當(dāng)前的 scrollLeft 來獲得同步。有一個麻煩的點(diǎn)是,我們不希望只能通過滾動表格體來實(shí)現(xiàn)表格滾動,也希望可以通過滾動表頭來實(shí)現(xiàn)表格的左右滾動,這就要求必須設(shè)置表頭的容器為 overflow-x: auto。
圖3:由于表格頭和表格體都需要橫向滾動,會引入兩個滾動條。
這顯然突破了大多數(shù)人對表格的認(rèn)知,橫向滾動會有兩個滾動條,一點(diǎn)都不美觀,需要我們在這個基礎(chǔ)之上進(jìn)行優(yōu)化。表格體上的橫向滾動條是沒有問題的,主要問題在于表格頭的,我們既希望能夠橫向滾動,又不想看到那個該死的滾動條,怎么辦呢?想辦法隱藏掉他就好了!首先我們設(shè)置表格頭的容器 overflow-x: scroll 以保證無論是否需要橫向滾動都會出現(xiàn)滾動條,方便我們簡化狀態(tài)的判斷。接下來我們可以再設(shè)置表格頭的容器 margin-bottom: -scrollBarWidth 來隱藏讓他的父級幫忙吞掉這個滾動條,一切就大功告成了。但令人頭大的是,滾動條的尺寸在不同瀏覽器,甚至是不同系統(tǒng)(例如 Windows 和 Mac 下的 chrome)中都是不一樣的! 我們無法很暴力地通過制定一個固定的值來做這件事,因此我們需要在表格渲染到頁面上去之后,主動去探測滾動條的寬度。
const scrollbarMeasure = { position: "absolute", top: "-9999px", width: "50px", height: "50px", overflow: "scroll", }; let scrollbarWidth; const measureScrollbar = () => { if (typeof document === "undefined" || typeof window === "undefined") { return 0; // 如果 document 不在,則證明不在瀏覽器環(huán)境,直接返回,兼容 node server render。 } if (scrollbarWidth) { return scrollbarWidth; // 滾動條在固定的環(huán)境下寬度不會改變,因此只做一次探測即可,優(yōu)化性能。 } const scrollDiv = document.createElement("div"); Object.keys(scrollbarMeasure).forEach((scrollProp) => { if (Object.prototype.hasOwnProperty.call(scrollbarMeasure, scrollProp)) { scrollDiv.style[scrollProp] = scrollbarMeasure[scrollProp]; } }); // 創(chuàng)造一個遠(yuǎn)離人世的帶滾動條的 div 用于探測,用戶對于此無感知。 document.body.appendChild(scrollDiv); const width = scrollDiv.offsetWidth - scrollDiv.clientWidth; // 獲取滾動條的寬度,offsetWidth 和 clientWidth 的區(qū)別,你能說清楚嗎? document.body.removeChild(scrollDiv); // 探測完成,銷毀測試元素,減少對頁面的影響。 scrollbarWidth = width; // 緩存結(jié)果,優(yōu)化性能 return scrollbarWidth; };遇到的問題 2:對不齊的表頭和數(shù)據(jù)
通過上面的方法,我們成功地隱藏了表格頭的橫向滾動條。稍微滾動一下,一切正常,一切都按照預(yù)想的執(zhí)行,直到一直滾動到頭,問題出現(xiàn)了,最后一列的表頭和下面的數(shù)據(jù)居然是對不齊的??!
這是怎么回事呢?圖4:當(dāng)表格體又可以左右滾動時,問題開始復(fù)雜起來~
原來,因?yàn)槲覀冊试S表格體上下滾動,使得在容器右側(cè)出現(xiàn)了一個縱向的滾動條。而表頭因?yàn)槲覀兿M枪潭ǖ模虼朔旁诹肆硪粋€容器中,這導(dǎo)致他不能共享這個滾動條。因此,這使得表格體拉到頭的時的 scrollLeft 也正好是表格頭到頭的位置,兩個到頭的位置上差了一個滾動條的寬度!看到這里,也許有的小伙伴可能會開始自己操練起來看看是不是這樣,然后發(fā)現(xiàn)并沒有類似問題,大呼坑爹,他們看到的情況大致如下圖:
圖4:Mac 某些設(shè)置下,看到的是另一份景象。
這引出了一個 Mac 下一個比較好玩的小設(shè)置,在 Mac 下滾動條何時顯示也可以配置,大致分為三類,具體的配置可以在 系統(tǒng)偏好設(shè)置 -> 通用 中看到。
當(dāng)我們選擇 滾動時 的時候,只有當(dāng)我們滾動一個元素的時候才會顯示滾動條,且這個滾動條是飄在內(nèi)容上,不會占據(jù)體積,于是便能看到 圖3 中的情景。這個兼容性上升到了系統(tǒng)的程度,在 PC 上還是比較少見的(笑)。
所以這個問題只會在這種情況下沒有出現(xiàn),在 Mac 下選擇 始終顯示,也同樣會出現(xiàn)。
那么如何解決這個問題呢?其實(shí),從上面的分析中我們也可以也大致地找到了問題的根源,表頭沒有共享表格體的縱向滾動條,那么我們只要想辦法解決這個就好了。這個說起來簡單,但畢竟不是在同一個容器中,如何共享呢?方案一:插入一個和滾動條相同寬度的 dom 元素充當(dāng)滾動條,但這會引起另一個問題就是表格頭和表格體的實(shí)際寬度不同,在各種滾動計(jì)算上引發(fā)很多麻煩。方案二:滾動條雖然在不同系統(tǒng)、不同瀏覽器里都不一樣尺寸,但卻有個優(yōu)點(diǎn)就是,只要在同一系統(tǒng)、統(tǒng)一瀏覽器里不管因?yàn)槭裁丛?,出現(xiàn)在什么地方,他的尺寸總是保持一致的。利用這個特點(diǎn),我們可以設(shè)置表頭容器的 overflow-y: scroll,總是包含一個縱向滾動條的道,這樣就兵不血刃的解決了這個麻煩的問題。
但這樣又引入了新的問題圖5:利用空的滾動條連接下面的滾動條來就可以解決上面的問題。
這樣雖然比較好的解決了表格體有滾動條的情況,但是如果表格體沒有滾動的情況下,遇到的問題就正好逆轉(zhuǎn)了,又會出現(xiàn)對不齊的情況!
圖6:當(dāng)表格體沒有縱向滾動條的情況下,又會出現(xiàn)新的問題。
解決這個問題也有幾種思路,簡單一點(diǎn)的思路可以模仿上面,給表格體也設(shè)置 overflow-y: scroll,這樣不管是否有滾動,看起來都是一樣而且不會錯位了。但是這種方法并不十分美觀,尤其在 windows 下顯得比較丑陋。于是在此方案基礎(chǔ)之上,我們加入了對于表格體縱向滾動的檢測,當(dāng)滾動區(qū)域的高度不大于容器高度時,即可認(rèn)為沒有滾動,此時同時設(shè)置表頭和表格體 overflow-y: hidden ,就可以達(dá)到解決上述問題,而在沒有滾動的情況下也保持美觀的要求。
遇到的問題3:列固定下的整體橫向滾動解決了上面兩個問題之后,一個完整的支持表頭固定和橫向滾動的表格就完成了。接下來又有了新的需求,要在原有表格基礎(chǔ)之上,支持左右側(cè)列固定。這也是表格中比較常見的需求,一些數(shù)據(jù)列或者操作列處于高頻使用下,希望能夠固定,這時就產(chǎn)生兩種處理表格體橫向滾動條的方式。
圖7:支持左右列固定后的方案 A 和 B
方案 A 模仿表頭的設(shè)計(jì)方案,將固定的列放在另一個容器當(dāng)中,把需要滾動的列多帶帶放置在一個可以滾動的容器當(dāng)中。這種方案的問題在于,固定列的寬度和列數(shù)都不像表頭一樣固定且較小。當(dāng)固定區(qū)域很大的時候,會嚴(yán)重?cái)D壓中間滾動區(qū)域滾動條的可操作區(qū)域,影響用戶體驗(yàn)。同時他也會遇到和表頭一樣,滾動至最后一行對不齊的情況。方案 B 則比較好的解決了方案 A 的第一個問題,左右側(cè)漂浮在表格的左右兩邊,覆蓋對應(yīng)的固定區(qū)域,橫向滾動條可以覆蓋整個表格,不會受到固定列寬度的限制。
方案 B 雖然好,但是仍有兩個問題需要解決如何實(shí)現(xiàn)在固定區(qū)域也可以做到整個表格上下滾動
漂浮的固定列會遮蓋住全局的橫向滾動條。
對于問題 1,他的解決思路其實(shí)和剛才表頭的解決思路類似。主要是通過適當(dāng)?shù)卦O(shè)置 margin 來隱藏本應(yīng)存在的滾動條,這里不再贅述。
對應(yīng)問題 2,如果是沒有縱向滾動,即 height: auto 這樣的情況下經(jīng)過測試不存在這個問題,float 的元素會很自然地不占用滾動條的體積,因此不會有遮擋。如果是有縱向滾動的,情況則復(fù)雜了一些,當(dāng) float 的元素和下面的主表格等高時會出現(xiàn)遮擋的情況。
圖8:方案 B 引入的遮擋滾動條的問題
那既然同高會有遮擋的問題,只要我們對應(yīng)的減掉對應(yīng)的滾動條高度就可以了,解決第一個問題時我們得到的大殺器,獲取滾動條的高度,也可以用在這里。這個方案也能很好地應(yīng)該對 Mac 下有的設(shè)置不顯示滾動條的情況,在不顯示滾動條的情況下,我們獲取到的寬度是 0,即沒有影響。而滾動時,滾動條會自動浮在最高的位置,因此仍然整條可見。
總結(jié)那么,今天,你的瀏覽器 “滾動” 了嗎?你是否已經(jīng)笑對其中了呢~
本文從實(shí)際的組件需求出發(fā),通過三個有關(guān)滾動條的問題出現(xiàn)和解決為線索,和大家分享了如何跨系統(tǒng)、跨瀏覽器地兼容滾動尤其是滾動條的問題,雖然是以表格為核心闡述,但解決方案不局限于表格之中,希望能給大家在遇到類似問題時提供一些靈感。文中涉及的行列固定相關(guān)的知識,因?yàn)榉潜疚闹攸c(diǎn),故一筆帶過,如果有興趣了解實(shí)現(xiàn)詳情,可以參考我們團(tuán)隊(duì)開源的 PC 端 UI 組件庫 UXCore 和對應(yīng)的組件 Table 里的代碼~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/87182.html
摘要:今天,你的瀏覽器滾動了嗎序在頁面中,一個有高度或者寬度的容器是最常見的構(gòu)成元素,而在其中的子元素有很大的概率超過父容器的尺寸限制,我們稱之為溢出。 今天,你的瀏覽器 滾動 了嗎? 序 在 Web 頁面中,一個有高度或者寬度的容器是最常見的構(gòu)成元素,而在其中的子元素有很大的概率超過父容器的尺寸限制,我們稱之為溢出。而應(yīng)對溢出,隱藏或者滾動是最常見的處理方式。滾動,作為 FEers 最經(jīng)常...
摘要:實(shí)戰(zhàn)之微信錢包騰訊服務(wù)界面網(wǎng)格布局是讓開發(fā)人員設(shè)計(jì)一個網(wǎng)格并將內(nèi)容放在這些網(wǎng)格內(nèi)。對于移動端適配,不同的公司不同的團(tuán)隊(duì)有不同的解決方案。柵格系統(tǒng)用于處理頁面多終端適配的問題。 grid實(shí)戰(zhàn)之微信錢包 騰訊服務(wù)界面 CSS3網(wǎng)格布局是讓開發(fā)人員設(shè)計(jì)一個網(wǎng)格并將內(nèi)容放在這些網(wǎng)格內(nèi)。而不是使用浮動制作一個網(wǎng)格,實(shí)際上是你將一個元素聲明為一個網(wǎng)格容器,并把元素內(nèi)容置于網(wǎng)格中。 移動端頁面適配—...
摘要:前端日報(bào)精選專題之函數(shù)柯里化前端可用性保障實(shí)踐入門指南頁面布局這個屬性你可能都不知道如何監(jiān)聽頁面變動并高效響應(yīng)發(fā)布中文深入理解筆記集合與集合第期介紹譯系統(tǒng)設(shè)計(jì)入門之面試題解答設(shè)計(jì)一個網(wǎng)頁爬蟲掘金基于構(gòu)建的移動端微應(yīng)用個人文章 2017-08-11 前端日報(bào) 精選 JavaScript專題之函數(shù)柯里化前端可用性保障實(shí)踐CSS入門指南-4:頁面布局 這5個CSS屬性你可能都不知道!如何監(jiān)聽...
摘要:二分析排查一步驟一使用搜索引擎我是在無意中發(fā)現(xiàn)該問題的,當(dāng)時觀察到的現(xiàn)象是綁定在上的事件有時會被觸發(fā),有時會失效。這說明并不存在偶爾失效的問題。也就是說,我需要找到確切的令響應(yīng)事件失效的原因。接下來的事很簡單,繼續(xù)搜索事件在頁面滾動后失效。 如果你關(guān)注我應(yīng)該知道,我最近對PC端頁面進(jìn)行移動適配。在這個過程中,為了節(jié)省用戶300ms的時間,同時給予用戶更及時的點(diǎn)擊反饋(這意味著更好的用戶...
閱讀 3328·2023-04-26 00:07
閱讀 3922·2021-11-23 10:08
閱讀 2940·2021-11-22 09:34
閱讀 859·2021-09-22 15:27
閱讀 1749·2019-08-30 15:54
閱讀 3743·2019-08-30 14:07
閱讀 913·2019-08-30 11:12
閱讀 678·2019-08-29 18:44