「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!」css
做者:JowayYoung
倉庫:Github、CodePen
博客:官網、掘金、思否、知乎
公衆號:IQ前端
特別聲明:原創不易,未經受權不得轉載或抄襲,如需轉載可聯繫筆者受權html
最近有些讀者悄悄發現了筆者的我的官網,無一例外都使用驚喜
、驚歎
等詞形容。沒錯,筆者使用大量CSS闡述了什麼叫作玩轉CSS的藝術之美。即便某些應用場景缺乏JS的加持,筆者也能將CSS玩得遊刃有餘,整個網站源碼裏CSS大概佔據60%
的份量,不少效果不是爲了炫技而是想告訴你們CSS的重要性與實用性。所以筆者想經過本文分享一些你們未必知道且超級實用的純CSS佈局排版技巧實現一些常見或特殊的佈局排版
。前端
開發每一張網頁都離不開佈局排版
,基於良好佈局排版
打下基礎,才能使後續的開發更順利。固然不能停留在IExplorer
時代那種侷限思惟上,沒辦法解決的佈局排版
都用JS實現😂。今時不一樣往日,現代CSS屬性能更好地快速實現各類佈局排版
,節約更多時間去摸魚😉。git
不過按照筆者目前瞭解的狀況來看,大部分同窗即便在無需兼容IExplorer
的狀況下仍是遵循CSS+JS
的方式完成一些常見或特殊的佈局排版
。從HTML/CSS/JS
前端三劍客的定位來看,HTML
映射網頁的結構,CSS
映射網頁的表現,JS
映射網頁的行爲。github
佈局排版指將圖形、文本、圖像、媒體等可視化信息元素在頁面佈局上調整位置
、尺寸
等屬性使頁面佈局變得條理化的過程。大部分同窗認爲佈局排版
就是幾個合理的CSS屬性隨便拼湊在一塊兒,但多數狀況即便實現也會存在瑕疵,此時就可能使用JS介入。web
從佈局排版
的特徵可知它屬於表現
範疇,所以筆者認爲大部分佈局排版
都能使用純CSS
完成,無需JS介入。segmentfault
本文秉承能使用CSS實現的效果都優先使用CSS的原則,爲你們講解筆者如何巧妙運用各類純CSS開發技巧完成一些常見或特殊的佈局排版
。所以筆者建議你們認真看一遍如下內容,絕對讓你有所收貨和驚喜。後端
若對CSS無特別想法,建議體驗如下網站,相信你會認真踏實地閱讀本文。數組
在進入主題前,筆者總結出佈局排版
一些必備屬性,這些屬性能快速搭建總體效果,再經過一些經常使用選擇器加以修飾達到完美效果。看似簡單,但使用起來不必定徹底駕馭。瀏覽器
必備屬性都是一些幾何屬性,主要用於聲明位置
和尺寸
。
float
position/left/right/top/bottom/z-index
display:flex/inline-flex
、flex系列屬性
box-sizing/margin/padding/border/width/height
選擇器因CSS模塊
衆多而派生出的數量也衆多,若無特別方式記熟這些選擇器對應的功能,也很難將其發揮到最大做用。
筆者根據選擇器的功能劃分出八大類,每一個類別的選擇器都能在一個應用場景裏互相組合,記熟這些類別的選擇器,相信就能將選擇器發揮到最大做用,也能遊刃有餘將其應用到一些常見或特殊的佈局排版
裏。
佈局排版
可能只應用到某些選擇器,但也不妨礙你們經過如下歸類方式記憶。選擇器做爲CSS的重要組成部分,比起屬性組合會有更多的玩法。
基礎選擇器
選擇器 | 別名 | 說明 | 版本 | 經常使用 |
---|---|---|---|---|
tag |
標籤選擇器 | 指定類型的標籤 |
1 | √ |
#id |
ID選擇器 | 指定身份的標籤 |
1 | √ |
.class |
類選擇器 | 指定類名的標籤 |
1 | √ |
* |
通配選擇器 | 全部類型的標籤 |
2 | √ |
層次選擇器
選擇器 | 別名 | 說明 | 版本 | 經常使用 |
---|---|---|---|---|
elemP elemC |
後代選擇器 |
元素的後代元素 |
1 | √ |
elemP>elemC |
子代選擇器 |
元素的子代元素 |
2 | √ |
elem1+elem2 |
相鄰同胞選擇器 |
元素相鄰的同胞元素 |
2 | √ |
elem1~elem2 |
通用同胞選擇器 |
元素後面的同胞元素 |
3 | √ |
集合選擇器
選擇器 | 別名 | 說明 | 版本 | 經常使用 |
---|---|---|---|---|
elem1,elem2 |
並集選擇器 |
多個指定的元素 |
1 | √ |
elem.class |
交集選擇器 |
指定類名的元素 |
1 | √ |
條件選擇器
選擇器 | 說明 | 版本 | 經常使用 |
---|---|---|---|
:lang |
指定標記語言的元素 |
2 | × |
:dir() |
指定編寫方向的元素 |
4 | × |
:has |
包含指定元素的元素 |
4 | × |
:is |
指定條件的元素 |
4 | × |
:not |
非指定條件的元素 |
4 | √ |
:where |
指定條件的元素 |
4 | × |
:scope |
指定元素 做爲參考點 |
4 | × |
:any-link |
全部包含href 的連接元素 |
4 | × |
:local-link |
全部包含href 且屬於絕對地址的連接元素 |
4 | × |
狀態選擇器
選擇器 | 說明 | 版本 | 經常使用 |
---|---|---|---|
:active |
鼠標激活的元素 |
1 | × |
:hover |
鼠標懸浮的元素 |
1 | √ |
:link |
未訪問的連接元素 |
1 | × |
:visited |
已訪問的連接元素 |
1 | × |
:target |
當前錨點的元素 |
3 | × |
:focus |
輸入聚焦的表單元素 |
2 | √ |
:required |
輸入必填的表單元素 |
3 | √ |
:valid |
輸入合法的表單元素 |
3 | √ |
:invalid |
輸入非法的表單元素 |
3 | √ |
:in-range |
輸入範圍之內的表單元素 |
3 | × |
:out-of-range |
輸入範圍之外的表單元素 |
3 | × |
:checked |
選項選中的表單元素 |
3 | √ |
:optional |
選項可選的表單元素 |
3 | × |
:enabled |
事件啓用的表單元素 |
3 | × |
:disabled |
事件禁用的表單元素 |
3 | √ |
:read-only |
只讀的表單元素 |
3 | × |
:read-write |
可讀可寫的表單元素 |
3 | × |
:target-within |
內部錨點元素處於激活狀態的元素 |
4 | × |
:focus-within |
內部表單元素處於聚焦狀態的元素 |
4 | √ |
:focus-visible |
輸入聚焦的表單元素 |
4 | × |
:blank |
輸入爲空的表單元素 |
4 | × |
:user-invalid |
輸入合法的表單元素 |
4 | × |
:indeterminate |
選項未定的表單元素 |
4 | × |
:placeholder-shown |
佔位顯示的表單元素 |
4 | √ |
:current() |
瀏覽中的元素 |
4 | × |
:past() |
已瀏覽的元素 |
4 | × |
:future() |
未瀏覽的元素 |
4 | × |
:playing |
開始播放的媒體元素 |
4 | × |
:paused |
暫停播放的媒體元素 |
4 | × |
結構選擇器
選擇器 | 說明 | 版本 | 經常使用 |
---|---|---|---|
:root |
文檔的根元素 |
3 | × |
:empty |
無子元素的元素 |
3 | √ |
:nth-child(n) |
元素中指定順序索引的元素 |
3 | √ |
:nth-last-child(n) |
元素中指定逆序索引的元素 |
3 | × |
:first-child |
元素中爲首的元素 |
2 | √ |
:last-child |
元素中爲尾的元素 |
3 | √ |
:only-child |
父元素僅有該元素的元素 |
3 | √ |
:nth-of-type(n) |
標籤中指定順序索引的標籤 |
3 | √ |
:nth-last-of-type(n) |
標籤中指定逆序索引的標籤 |
3 | × |
:first-of-type |
標籤中爲首的標籤 |
3 | √ |
:last-of-type |
標籤中爲尾標籤 |
3 | √ |
:only-of-type |
父元素僅有該標籤的標籤 |
3 | √ |
屬性選擇器
選擇器 | 說明 | 版本 | 經常使用 |
---|---|---|---|
[attr] |
指定屬性的元素 |
2 | √ |
[attr=val] |
屬性等於指定值的元素 |
2 | √ |
[attr*=val] |
屬性包含指定值的元素 |
3 | √ |
[attr^=val] |
屬性以指定值開頭的元素 |
3 | √ |
[attr$=val] |
屬性以指定值結尾的元素 |
3 | √ |
[attr~=val] |
屬性包含指定值(完整單詞)的元素 (不推薦使用) |
2 | × |
[attr|=val] |
屬性以指定值(完整單詞)開頭的元素 (不推薦使用) |
2 | × |
僞元素
選擇器 | 說明 | 版本 | 經常使用 |
---|---|---|---|
::before |
在元素前插入的內容 |
2 | √ |
::after |
在元素後插入的內容 |
2 | √ |
::first-letter |
元素的首字母 |
1 | × |
::first-line |
元素的首行 |
1 | × |
::selection |
鼠標選中的元素 |
3 | × |
::backdrop |
全屏模式的元素 |
4 | × |
::placeholder |
表單元素的佔位 |
4 | √ |
有了上述前置知識,接下來跟着筆者體驗一次如何巧妙運用各類純CSS開發技巧完成一些常見或特殊的佈局排版
吧。爲了方便瀏覽器自動計算某些樣式,需全局設置box-sizing:border-box
,編碼前請引入筆者整理的reset.css。
主體佈局指在大部分狀況下通用且具有統一特徵的佔位佈局。掌握主體佈局
是一個前端必不可少的技能,養成看設計圖就能大概規劃出總體佈局的前提是必須熟悉這些主體佈局
的特色與構造。
經典的全屏佈局由頂部
、底部
和主體
三部分組成,其特色爲三部分左右滿屏拉伸
、頂部底部高度固定
和主體高度自適應
。該佈局很常見,也是大部分Web應用主體的主流佈局。一般使用<header>
、<footer>
和<main>
三個標籤語義化排版,<main>
內還可插入<aside>
側欄或其餘語義化標籤。
<div class="fullscreen-layout">
<header></header>
<main></main>
<footer></footer>
</div>
複製代碼
position + left/right/top/bottom
三部分統一聲明left:0
和right:0
將其左右滿屏拉伸;頂部和底部分別聲明top:0
和bottom:0
將其吸頂和吸底,並聲明倆高度爲固定值;將主體的top
和bottom
分別聲明爲頂部高度和底部高度。經過絕對定位的方式將三部分固定在特定位置,使其互不影響。
.fullscreen-layout {
position: relative;
width: 400px;
height: 400px;
header,
footer,
main {
position: absolute;
left: 0;
right: 0;
}
header {
top: 0;
height: 50px;
background-color: #f66;
}
footer {
bottom: 0;
height: 50px;
background-color: #66f;
}
main {
top: 50px;
bottom: 50px;
background-color: #3c9;
}
}
複製代碼
flex
使用flex
實現會更簡潔。display:flex
默認會令子節點橫向排列,需聲明flex-direction:column
改變子節點排列方向爲縱向排列;頂部和底部高度固定,因此主體需聲明flex:1
讓高度自適應。
.fullscreen-layout {
display: flex;
flex-direction: column;
width: 400px;
height: 400px;
header {
height: 50px;
background-color: #f66;
}
footer {
height: 50px;
background-color: #66f;
}
main {
flex: 1;
background-color: #3c9;
}
}
複製代碼
若<main>
需表現成可滾動狀態,千萬不要聲明overflow:auto
讓容器自適應滾動,這樣作有可能由於其餘格式化上下文的影響而致使自適應滾動失效或產生其餘未知效果。需在<main>
內插入一個<div>
並聲明以下。
div {
overflow: hidden;
height: 100%;
}
複製代碼
經典的兩列布局由左右兩列
組成,其特色爲一列寬度固定
、另外一列寬度自適應
和兩列高度固定且相等
。如下以左列寬度固定和右列寬度自適應爲例,反之同理。
<div class="two-column-layout">
<div class="left"></div>
<div class="right"></div>
</div>
複製代碼
float + margin-left/right
左列聲明float:left
和固定寬度,因爲float
使節點脫流,右列需聲明margin-left
爲左列寬度,以保證兩列不會重疊。
.two-column-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
margin-left: 100px;
height: 100%;
background-color: #66f;
}
}
複製代碼
overflow + float
左列聲明同上,右列聲明overflow:hidden
使其造成BFC區域
與外界隔離。
.two-column-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
overflow: hidden;
height: 100%;
background-color: #66f;
}
}
複製代碼
flex
使用flex
實現會更簡潔。左列聲明固定寬度,右列聲明flex:1
自適應寬度。
.two-column-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 100px;
background-color: #f66;
}
.right {
flex: 1;
background-color: #66f;
}
}
複製代碼
經典的三列布局由左中右三列
組成,其特色爲連續兩列寬度固定
、剩餘一列寬度自適應
和三列高度固定且相等
。如下以左中列寬度固定和右列寬度自適應爲例,反之同理。總體的實現原理與上述兩列布局一致。
<div class="three-column-layout">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
複製代碼
爲了讓右列寬度自適應計算,就不使用float + margin-left
的方式了,若使用margin-left
還得結合左中列寬度計算。
overflow + float
.three-column-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 50px;
height: 100%;
background-color: #f66;
}
.center {
float: left;
width: 100px;
height: 100%;
background-color: #66f;
}
.right {
overflow: hidden;
height: 100%;
background-color: #3c9;
}
}
複製代碼
flex
.three-column-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 50px;
background-color: #f66;
}
.center {
width: 100px;
background-color: #66f;
}
.right {
flex: 1;
background-color: #3c9;
}
}
複製代碼
經典的聖盃佈局和雙飛翼佈局都是由左中右三列
組成,其特色爲左右兩列寬度固定
、中間一列寬度自適應
和三列高度固定且相等
。其實也是上述兩列布局和三列布局的變體,總體的實現原理與上述N列布局一致,可能就是一些細節需注意。
聖盃佈局
和雙飛翼佈局
在大致相同下也存在一點不一樣,區別在於雙飛翼佈局
中間列需插入一個子節點。在常規實現方式裏也是在這個中間列裏作文章,如何使中間列內容不被左右列遮擋
。
float
和margin負值
將其拉回與中間列處在同一水平線上padding
爲左右列留出空位,將左右列固定在空位上margin
爲左右列讓出空位,將左右列固定在空位上聖盃佈局float + margin-left/right + padding-left/right
因爲浮動節點在位置上不能高於前面或平級的非浮動節點,不然會致使浮動節點下沉。所以在編寫HTML結構時,將中間列節點挪到右列節點後面。
<div class="grail-layout-x">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
複製代碼
.grail-layout-x {
padding: 0 100px;
width: 400px;
height: 400px;
.left {
float: left;
margin-left: -100px;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
float: right;
margin-right: -100px;
width: 100px;
height: 100%;
background-color: #66f;
}
.center {
height: 100%;
background-color: #3c9;
}
}
複製代碼
雙飛翼佈局float + margin-left/right
HTML結構大致同上,只是在中間列裏插入一個子節點<div>
。根據二者區別,CSS聲明會與上述聖盃佈局有一點點出入,可觀察對比找出不一樣地方。
<div class="grail-layout-y">
<div class="left"></div>
<div class="right"></div>
<div class="center">
<div></div>
</div>
</div>
複製代碼
.grail-layout-y {
width: 400px;
height: 400px;
.left {
float: left;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
float: right;
width: 100px;
height: 100%;
background-color: #66f;
}
.center {
margin: 0 100px;
height: 100%;
background-color: #3c9;
}
}
複製代碼
聖盃佈局/雙飛翼佈局flex
使用flex實現聖盃佈局/雙飛翼佈局
可忽略上述分析,左右兩列寬度固定,中間列寬度自適應。
<div class="grail-layout">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
複製代碼
.grail-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 100px;
background-color: #f66;
}
.center {
flex: 1;
background-color: #3c9;
}
.right {
width: 100px;
background-color: #66f;
}
}
複製代碼
經典的均分佈局由多列
組成,其特色爲每列寬度相等
和每列高度固定且相等
。整體來講也是最簡單的經典佈局,因爲每列寬度相等,因此很易找到合適的方式處理。
<div class="average-layout">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
<div class="four"></div>
</div>
複製代碼
.one {
background-color: #f66;
}
.two {
background-color: #66f;
}
.three {
background-color: #f90;
}
.four {
background-color: #09f;
}
複製代碼
float + width
每列寬度聲明爲相等的百分比,如有4列則聲明width:25%
。N列就用公式100 / n
求出最終百分比寬度,記得保留2位小數,懶人還可用width:calc(100% / n)
自動計算呢。
.average-layout {
width: 400px;
height: 400px;
div {
float: left;
width: 25%;
height: 100%;
}
}
複製代碼
flex
使用flex實現會更簡潔。節點聲明display:flex
後,生成的FFC容器
裏全部子節點的高度都相等,由於容器的align-items
默認爲stretch
,全部子節點將佔滿整個容器的高度。每列聲明flex:1
自適應寬度。
.average-layout {
display: flex;
width: 400px;
height: 400px;
div {
flex: 1;
}
}
複製代碼
居中佈局由父容器
與若干個子容器
組成,子容器在父容器中橫向排列或豎向排列且呈水平居中或垂直居中。居中佈局是一個很經典的問題,相信你們都會常常遇到。
在此直接上一個目前最簡單最高效的居中方式。display:flex
與margin:auto
的強行組合,同窗們自行體會。
<div class="center-layout">
<div></div>
</div>
複製代碼
.center-layout {
display: flex;
width: 400px;
height: 400px;
background-color: #f66;
div {
margin: auto;
width: 100px;
height: 100px;
background-color: #66f;
}
}
複製代碼
自適佈局指相對視窗任何尺寸都能佔據特定百分比的佔位佈局。自適佈局
的容器都是根據視窗尺寸計算,即便父節點
或祖先節點
的尺寸發生變化也不會影響自適佈局
的容器尺寸。
搭建自適佈局
就離不開視窗比例單位。在CSS3裏增長了與viewport
相關的四個長度單位,隨着時間推移,目前大部分瀏覽器對這四個長度單位都有較好的兼容性,這也是將來最建議在伸縮方案裏使用的長度單位。
1vw
表示1%
視窗寬度1vh
表示1%
視窗高度1vmin
表示1%
視窗寬度和1%
視窗高度裏最小者1vmax
表示1%
視窗寬度和1%
視窗高度裏最大者視窗寬高在JS裏分別對應window.innerWdith
和window.innerHeight
。若不考慮低版本瀏覽器兼容性,徹底可用一行CSS代碼秒殺全部移動端的伸縮方案。
/* 基於UI width=750px DPR=2的網頁 */
html {
font-size: calc(100vw / 7.5);
}
複製代碼
上述代碼使用calc()
實現font-size
的動態計算。calc()
是自適佈局
裏的核心存在,無它就不能愉快地實現自適佈局
全部動態計算了。
calc()
用於動態計算單位,數值
、長度
、角度
、時間
和百分比
都能做爲參數。因爲執行數學表達式
後返回運算後的計算值,因此可減小大量人工計算甚至無需人工計算。
calc()
飢不擇食,全部計量單位都能做爲參數參加整個動態計算。
整數
、浮點數
px
、em
、rem
、vw
、vh
等deg
、turn
s
、ms
%
calc()
雖然好用,但新手不免會遇到一些坑,謹記如下特色,相信就能玩轉calc()
了。
+
、-
、*
、/
做爲運算符號()
提高運算等級空格
間隔起來第三點尤其重要,若未能遵照,瀏覽器直接忽略該屬性。
上述font-size:calc(100vw / 7.5)
其實就是根據設計圖與瀏覽器視窗的比例動態計算<html>
的font-size
:100/750 = x/100vw
。
在SPA裏有遇過由於有滾動條或無滾動條而致使頁面路由在跳轉過程裏發生向左或向右的抖動嗎?這讓強迫症患者很難受,此時可用calc()
巧妙解決該問題。
.elem {
padding-right: calc(100vw - 100%);
}
複製代碼
不直接聲明padding-right
爲滾動條寬度是由於每一個瀏覽器的默認滾動條寬度均可能不一致。100vw
是視窗寬度,100%
內容寬度,那麼100vw - 100%
就是滾動條寬度,聲明padding-right
用於保留滾動條出現的位置,這樣滾動條出不出現都不會讓頁面抖動了。
有了calc()
作保障就可迅速實現一些與視窗尺寸相關的佈局了。例如實現一個視窗寬度都爲50%
的彈窗。
<div class="modal">
<div class="modal-wrapper"></div>
</div>
複製代碼
.modal {
display: flex;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, .5);
&-wrapper {
width: 50vw;
height: 200px;
background-color: #f66;
}
}
複製代碼
固然使用calc()
也不必定結合視窗比例單位
計算。例如自適佈局
已知部分節點高度,不想手動計算最後節點高度但又想其填充佈局剩餘空間。
<ul class="selfadaption-layout">
<div class="box-1"></div>
<div class="box-2"></div>
<div class="box-3"></div>
</ul>
複製代碼
.selfadaption-layout {
width: 200px;
height: 567px;
.box-1 {
height: 123px;
background-color: #f66;
}
.box-2 {
height: 15%;
background-color: #3c9;
}
.box-3 {
height: calc(100% - 123px - 15%);
background-color: #09f;
}
}
複製代碼
吸附佈局指相對視窗任何滾動都能佔據特定位置的佔位佈局。視窗滾動到特定位置,佈局固定在該位置,後續不隨視窗滾動而滾動。該佈局產生的效果俗稱吸附效果
,是一種常見網頁效果。譬如吸頂效果
和吸底效果
都是該範疇,常常在跟隨導航
、移動廣告
和懸浮提示
等應用場景裏出現。
在jQuery時代
就有不少吸附效果插件,如今三大前端框架也有自身第三方的吸附效果組件。它們都有着共通的實現原理:監聽scroll
事件,判斷scrollTop
和目標節點
的位置範圍,符合條件則將目標節點
的position
聲明爲fixed
使目標節點
相對於視窗定位,讓用戶看上去就像釘在視窗指定位置上。
JS實現吸附效果的代碼在網上一搜一大堆,更況且筆者喜歡耍CSS,在此就不貼相關的JS代碼了。在此推薦一個不多見不多用的CSS屬性position:sticky
。簡單的兩行核心CSS代碼就能完成十多行核心JS代碼的功能,何樂而不爲呢。
簡單回顧position
屬性值,怎樣用就不說了,你們應該都熟悉。
取值 | 功能 | 版本 |
---|---|---|
inherit | 繼承 |
2 |
static | 標準流 |
2 |
relative | 相對定位 |
2 |
absolute | 絕對定位 |
2 |
fixed | 固定定位 |
2 |
sticky | 粘性定位 |
3 |
當值爲sticky
時將節點變成粘性定位
。粘性定位是相對定位
和固定定位
的結合體,節點在特定閾值
跨越前爲相對定位
,跨越後爲固定定位
。
<div class="adsorption-position">
<ul>
<li>Top 1</li>
<li>Top 2</li>
<li>Normal</li>
<li>Bottom 1</li>
<li>Bottom 2</li>
</ul>
</div>
複製代碼
.adsorption-position {
overflow: auto;
position: relative;
width: 400px;
height: 280px;
outline: 1px solid #3c9;
ul {
padding: 200px 0;
}
li {
position: sticky;
height: 40px;
line-height: 40px;
text-align: center;
color: #fff;
&:nth-child(1) {
top: 0;
z-index: 9;
background-color: #f66;
}
&:nth-child(2) {
top: 40px;
z-index: 9;
background-color: #66f;
}
&:nth-child(3) {
background-color: #f90;
}
&:nth-child(4) {
bottom: 0;
z-index: 9;
background-color: #09f;
}
&:nth-child(5) {
bottom: 40px;
z-index: 9;
background-color: #3c9;
}
}
}
複製代碼
兩行核心CSS代碼分別是position:sticky
和top/bottom:npx
。上述5個節點都聲明position:sticky
,但因爲top/bottom
賦值有所不一樣就產生不一樣吸附效果。
細心的同窗可能發現這些節點在某些滾動時刻處於相對定位,在特定滾動時刻處於固定定位
。
<li>
:top
爲0px
,滾動到容器頂部
就固定<li>
:top
爲40px
,滾動到距離容器頂部40px
就固定<li>
:未聲明top/bottom
,就一直保持相對定位<li>
:bottom
爲40px
,滾動到距離容器底部40px
就固定<li>
:bottom
爲0px
,滾動到容器底部
就固定固然,換成left
或right
也同樣能實現橫向的吸附效果
。
值得注意,粘性定位
的參照物並不必定是position:fixed
。當目標節點
的任意祖先節點
都未聲明position:relative|absolute|fixed|sticky
,才與position:fixed
表現一致。當離目標節點
最近的祖先節點
聲明position:relative|absolute|fixed|sticky
,目標節點
就相對該祖先節點
產生粘性定位
。簡單來講確認參照物的方式與position:absolute
一致。
兼容性勉強還行,近2年發版的瀏覽器都能支持,Safari
和Firefox
的兼容性仍是挺讚的。有吸附效果
需求的同窗建議一試,要兼容IExplorer
就算了。期待該屬性有更好的發展,畢竟吸附佈局
真的是一種常見佈局。
橫向佈局指容器內節點以水平方向排列且溢出部分被隱藏的佔位佈局。豎向佈局
很常見,聲明overflow:hidden;width:xpx;height:ypx
就能實現,但橫向佈局
卻不能使用相似方式實現。
爲了方便使用多種方式實現橫向佈局
,如下將通用代碼拆分出來。
<div class="horizontal-layout">
<ul>
<li>Alibaba</li>
<li>Tencent</li>
<li>Baidu</li>
<li>Jingdong</li>
<li>Ant</li>
<li>Netease</li>
<li>Meituan</li>
<li>ByteDance</li>
<li>360</li>
<li>Sina</li>
</ul>
</div>
複製代碼
.horizontal-layout {
overflow: hidden;
width: 300px;
height: 100px;
ul {
overflow-x: auto;
cursor: pointer;
&::-webkit-scrollbar {
height: 10px;
}
&::-webkit-scrollbar-track {
background-color: #f0f0f0;
}
&::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: #f66;
}
}
li {
overflow: hidden;
height: 90px;
background-color: #66f;
line-height: 90px;
text-align: center;
font-size: 16px;
color: #fff;
&:not(:first-child) {
margin-left: 10px;
}
}
}
複製代碼
有些同窗可能會使用行內元素
實現橫向排版,但必須聲明overflow-y:hidden
使容器在Y軸
方向隱藏溢出部分。因爲行內元素
在當前行排版產生溢出會自動將其他節點排版到下一行,所以還需聲明white-space:nowrap
使全部行內元素
在一行內排版完畢。若產生滾動條,還需對容器的height
作適當的微調。
.horizontal-layout.inline {
height: 102px;
ul {
overflow-y: hidden;
white-space: nowrap;
}
li {
display: inline-block;
width: 90px;
}
}
複製代碼
上述方式在筆者在開發認知裏以爲太繁瑣,實質上將全部節點當成文本排列,也是醉了。筆者推薦使用flex佈局
完成上述佈局,flex佈局
做爲目前最多見的佈局方式
,相信也不用筆者多說。如下實現方式不知你們是否見過呢?在移動端上體驗會更棒喔!
.horizontal-layout.flex {
ul {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
}
li {
flex-shrink: 0;
flex-basis: 90px;
}
}
複製代碼
凸顯佈局指容器內節點以同一方向排列且存在一個節點在某個方向上較突出的佔位佈局。該佈局描述起來可能比較拗口,直接看如下效果吧,這是一個橫向列表,節點從左往右排列,最右邊的節點特別突出。這就是凸顯佈局
的特徵,凸顯的節點可在凸顯佈局
任意位置,上下左右
,左上左下右上右下
都行。
這裏巧妙運用margin-*:auto
實現了凸顯佈局
。相信你們實現水平居中固定寬度的塊元素
都會使用margin:0 auto
。
在此一樣原理,當節點聲明margin-*:auto
時,瀏覽器會自動計算剩餘空間並將該值賦值給該節點。在使用該技巧時必須基於flex佈局
。
<ul class="highlight-layout">
<li>Alibaba</li>
<li>Tencent</li>
<li>Baidu</li>
<li>Jingdong</li>
<li>Ant</li>
<li>Netease</li>
</ul>
複製代碼
.highlight-layout {
display: flex;
align-items: center;
padding: 0 10px;
width: 600px;
height: 60px;
background-color: #3c9;
li {
padding: 0 10px;
height: 40px;
background-color: #3c9;
line-height: 40px;
font-size: 16px;
color: #fff;
}
&.left li {
&:not(:first-child) {
margin-left: 10px;
}
&:last-child {
margin-left: auto;
}
}
&.right li {
&:not(:last-child) {
margin-right: 10px;
}
&:first-child {
margin-right: auto;
}
}
&.top {
flex-direction: column;
width: 120px;
height: 400px;
li {
&:not(:first-child) {
margin-top: 10px;
}
&:last-child {
margin-top: auto;
}
}
}
&.bottom {
flex-direction: column;
width: 120px;
height: 400px;
li {
&:not(:last-child) {
margin-bottom: 10px;
}
&:first-child {
margin-bottom: auto;
}
}
}
}
複製代碼
在此還有一個小技巧,那就是:not()
與:first-child
和:last-child
的巧妙運用。這樣組合讓特殊位置的節點直接減小屬性覆蓋的問題,不只易讀還能裝逼。
間距佈局指容器內節點從左往右從上往下排列且以特定間距間隔的佔位佈局。間距佈局
常見於各大列表,是筆者認爲最重要的佈局之一。爲什麼如此簡單的佈局仍是花費一些篇幅講解呢?最近筆者查看了Github
上不少與間隔佈局
相關的CSS代碼,雖然總體效果看上去無大礙,但margin/padding
和結構選擇器
卻亂用,所以筆者想從零到一糾正間距佈局
的正確編碼方式。
在進入編碼環節前,筆者想重點講解:nth-child()
的點睛之筆。大部分同窗可能只認得:nth-child(n)
、:nth-child(2n-1)
、:nth-child(2n)
和:nth-child(xn)
的平常用法,但其實還有一些你可能未見過的用法。在此筆者藉此次機會將:nth-child()
全部用法總結下,n/x/y
表明正整數,最小值爲1
。
n
個元素奇數位置
元素,至關於:nth-child(2n-1)
偶數位置
元素,至關於:nth-child(2n)
x*n
個元素x
個元素x~y
個元素分析間距佈局
的一切特色,捕獲特徵頗有利於將特徵轉換成CSS代碼。
margin
聲明padding
聲明,後續方便聲明background-color
(該步驟很易與上一步驟混淆,請特別注意)padding
聲明容器而不是使用margin
聲明節點(該步驟說明上一步驟的處理結果)margin-left/margin-right
(二選一)聲明節點margin-left:0
聲明最左列節點或使用margin-right:0
聲明最右列節點margin-top
聲明其他節點border-top/border-bottom
代替padding-top/padding-bottom
所有步驟串聯起來理解可能會產生混亂,但結合如下代碼理解相信就能很快熟悉。以一行排列3個節點總共8個節點爲例,最終效果爲三行三列。
<ul class="spacing-layout">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
複製代碼
.spacing-layout {
display: flex;
overflow: auto;
flex-wrap: wrap;
margin-top: 20px; // 對應A
padding: 20px; // 對應B和C
// padding-top: 0; // 對應G
// padding-bottom: 0; // 對應G
// border-top: 20px solid #f66; // 對應G
// border-bottom: 20px solid #f66; // 對應G
width: 700px; // 稍微留空用於顯示滾動條
height: 400px;
background-color: #f66;
li {
width: 200px;
height: 200px;
background-color: #66f;
line-height: 200px;
text-align: center;
font-size: 20px;
color: #fff;
&:not(:nth-child(3n)) {
margin-right: 20px; // 對應D和E
}
&:nth-child(n+4) {
margin-top: 20px; // 對應F
}
}
}
複製代碼
空載佈局指容器內無任何節點時使用其餘形式代替的佔位佈局。還有使用JS判斷列表集合爲空時顯示佔位符嗎?相信不少使用MVVM框架開發的同窗都會使用條件判斷的方式渲染虛擬DOM,若列表長度不爲0則渲染列表,不然渲染佔位符。
<div>
<ul v-if="list.length">...</ul>
<div v-esle>Empty</div>
</div>
複製代碼
然而CSS提供一個空判斷的選擇器:empty
,這應該不多同窗會注意到吧。
:empty
做用於無子節點的節點,該子節點也包括行內匿名盒(單獨的文本內容
)。如下三種狀況均視爲非空狀態,若不出現這三種狀態則視爲空狀態,此時:empty
纔會觸發。
<div><p>CSS</p></div>
<div>CSS</div>
<div>Hello <p>CSS</p></div>
<ul class="empty-layout">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
<ul class="empty-layout"></ul>
複製代碼
$empty: "https://yangzw.vip/img/empty.svg";
.empty-layout {
overflow: auto;
width: 200px;
height: 150px;
outline: 1px solid #3c9;
&:empty {
display: flex;
justify-content: center;
align-items: center;
background: url($empty) no-repeat center/100px auto;
&::after {
margin-top: 90px;
font-weight: bold;
content: "沒錢就沒數據";
}
}
li {
padding: 0 10px;
height: 30px;
background-color: #09f;
line-height: 30px;
color: #fff;
&:nth-child(even) {
background-color: #f90;
}
}
}
複製代碼
另外還存在一種特殊的空載佈局
,就是不作任何處理。這樣最終渲染的DOM只有容器,若已聲明margin/padding/border
但未聲明width/height
的狀況下,就會出現如下佔位效果。無任何子節點的容器還聲明着margin/padding/border
,看着都尷尬。
沒事,:empty
幫你搞掂!對於無任何子節點的容器直接聲明display:none
解決全部無效佔位,固然也可做用於指定節點。一招制敵,勁!
// 做用於全部節點
:empty {
display: none;
}
// 做用於指定節點
.empty-layout:empty {
display: none;
}
複製代碼
多格佈局指容器內節點以動態數量的格子形式排列的佔位佈局。微信朋友圈的相冊就是最多見的多格佈局
了,當單張照片排列、兩張照片排列、三張照片排列等等,每種狀況下照片的尺寸均可能不一致。筆者製做了一個動態多格相冊懷念我家狗狗AB。你們感覺下純CSS實現動態數量的多格佈局
吧。
在此留個懸念,不講解如何實現,看看你們能不能根據筆者列出的提示嘗試將該效果復原。主要原理是根據結構選擇器限制節點範圍
實現,在本文也可找到原理的答案喔!記得實現完再看如下源碼哈!
<ul class="multigrid-layout">
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
</ul>
複製代碼
@mixin square($count: 2) {
$length: calc((100% - #{$count} * 10px) / #{$count});
width: $length;
height: $length;
}
.multigrid-layout {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-content: flex-start;
padding: 5px;
border: 1px solid #ccc;
border-radius: 5px;
width: 400px;
height: 400px;
li {
display: flex;
overflow: hidden;
justify-content: center;
margin: 5px;
background-color: #f0f0f0;
@include square(3);
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// 一個元素
.item:only-child {
border-radius: 10px;
width: auto;
max-width: 80%;
height: auto;
max-height: 80%;
}
// 兩個元素
.item:first-child:nth-last-child(2),
.item:first-child:nth-last-child(2) ~ .item:nth-child(2) {
@include square(2);
}
.item:first-child:nth-last-child(2) {
border-radius: 10px 0 0 10px;
}
.item:first-child:nth-last-child(2) ~ .item:nth-child(2) {
border-radius: 0 10px 10px 0;
}
// 三個元素
.item:first-child:nth-last-child(3),
.item:first-child:nth-last-child(3) ~ .item:nth-child(2),
.item:first-child:nth-last-child(3) ~ .item:nth-child(3) {
@include square(2);
}
.item:first-child:nth-last-child(3) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(3) ~ .item:nth-child(2) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(3) ~ .item:nth-child(3) {
border-bottom-left-radius: 10px;
}
// 四個元素
.item:first-child:nth-last-child(4),
.item:first-child:nth-last-child(4) ~ .item:nth-child(2),
.item:first-child:nth-last-child(4) ~ .item:nth-child(3),
.item:first-child:nth-last-child(4) ~ .item:nth-child(4) {
@include square(2);
}
.item:first-child:nth-last-child(4) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(4) ~ .item:nth-child(2) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(4) ~ .item:nth-child(3) {
border-bottom-left-radius: 10px;
}
.item:first-child:nth-last-child(4) ~ .item:nth-child(4) {
border-bottom-right-radius: 10px;
}
// 五個元素
.item:first-child:nth-last-child(5) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(5) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(5) ~ .item:nth-child(4) {
border-bottom-left-radius: 10px;
}
// 六個元素
.item:first-child:nth-last-child(6) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(6) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(6) ~ .item:nth-child(4) {
border-bottom-left-radius: 10px;
}
.item:first-child:nth-last-child(6) ~ .item:nth-child(6) {
border-bottom-right-radius: 10px;
}
// 七個元素
.item:first-child:nth-last-child(7) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(7) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(7) ~ .item:nth-child(7) {
border-bottom-left-radius: 10px;
}
// 八個元素
.item:first-child:nth-last-child(8) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(8) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(8) ~ .item:nth-child(7) {
border-bottom-left-radius: 10px;
}
// 九個元素
.item:first-child:nth-last-child(9) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(9) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(9) ~ .item:nth-child(7) {
border-bottom-left-radius: 10px;
}
.item:first-child:nth-last-child(9) ~ .item:nth-child(9) {
border-bottom-right-radius: 10px;
}
複製代碼
不少同窗可能以爲CSS很簡單,但真正玩起來也能與JS有得一比。筆者從事前端領域多年,一直致力於CSS技術的研究與應用,固然真的不是爲了玩,而是在玩的過程裏將實踐到的知識充分應用於工做上。
JS重要但CSS一樣重要,但願喜歡CSS的同窗多多關注筆者,相信你必定會有更多CSS方面的收穫。在你不太願意學習CSS時,請瀏覽如下網站,相信你會有不一樣的體驗。
筆者更多的CSS開發經驗已撰寫成掘金小冊《玩轉CSS的藝術之美》,做爲一本小衆的小冊同時也是掘金社區裏惟一一本關於CSS的小冊,相信關注CSS的你必定會喜歡。筆者已向小冊姐姐申請了100份
小冊6折優惠碼OGecoefC,喜歡CSS的同窗可瞭解下喔。
筆者往期過萬閱讀量的掘金爆文
16.7w
閱讀量,4500+
點贊量14.1w
閱讀量,4600+
點贊量5.9w
閱讀量,3800+
點贊量5.5w
閱讀量,1700+
點贊量2.9w
閱讀量,1800+
點贊量2.9w
閱讀量,900+
點贊量2.1w
閱讀量,700+
點贊量1.8w
閱讀量,600+
點贊量1.1w
閱讀量,200+
點贊量1.1w
閱讀量,200+
點贊量❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更多高質量文章
關注公衆號IQ前端
,一個專一於CSS/JS開發技巧的前端公衆號,更多前端小乾貨等着你喔
資料
免費領取學習資料進羣
拉你進技術交流羣IQ前端
,更多CSS/JS開發技巧只在公衆號推送