我們可以用CSS檢查,以了解一組元素的數量是否小于或等于一個數字。例如,一個擁有三個或更多子項的grid。你可能會想,為什么需要這樣做呢?在某些情況下,一個組件或一個布局可能會根據子元素的數量而改變。
這在CSS中已經存在很多年了,但現在通過CSS :has,它變得更加強大。我們可以把nth-last-child選擇器和:has結合起來,以達到神奇的效果!你沒聽錯。
在這篇文章中,我將強調幾個例子,說明我們可以將一個CSS選擇器和:has結合起來,形成一個有條件的組件/布局狀態。
這篇文章的主要要素之一是:nth-last-child偽類。我們可以使用該選擇器來模擬計算子元素。
來看看它是如何工作的。我將盡可能用直白的話來解釋。
請看下圖:
圖片
我們有一個五個卡片的列表。我們將用這個例子來證明我們可以用:nth-last-child做什么。
在下列CSS中,n + 3意味著:
li:nth-last-child(n + 3) { /* styles */}
從末端選擇前三項,從第三項開始計算。
讓我們仔細看看。首先,我們需要從末端計算三個項。這樣一來,第三項實際上就是我們從末端開始計算的第一項。
圖片
我們從第三項算起直到最后,這里是被選中的項:
圖片
我們可以使用:nth-last-child作為CSS的數量查詢。
請看下圖:
圖片
我們有一個信息清單,當我們有5個或更多的項時,它的顯示方式會不同。
<ul> <li></li> <li></li> <li></li> <!-- more items --></ul>
li { /* default styles */}/* If the list has 5 or more items */li:nth-last-child(n + 5),li:nth-last-child(n + 5) ~ li { width: 50%; display: inline-block; border-bottom: 0;}
雖然這很有效,但在某些方面仍然有點局限性。
想象一下,當有5個或更多的項時,我們需要為每個<li>添加display: flex。我們不能用 :nth-last-child 偽類選擇器來做這個。
原因是,添加display: flex將迫使每個項留在自己的行中,這與要實現的設計不一致。
li:nth-last-child(n + 5),li:nth-last-child(n + 5) ~ li { width: 50%; display: flex; flex-direciton: column;}
圖片
我們可以用display: inline-flex來解決這個問題,但對我來說,這仍然不是最佳解決方案。原因是,瀏覽器會考慮到HTML元素之間的間距,它們應該是這樣的:
<ul> <li></li><li></li><li></li> <!-- more items --></ul>
如果我們不這樣做,display: inline-flex的效果將與display: flex相同。解決這個問題的一個方法是將寬度減少1%。
li:nth-last-child(n + 5),li:nth-last-child(n + 5) ~ li { width: 49%; display: flex; flex-direciton: column;}
如果沒有對父類進行控制的能力,就不能那么直接地對列表的布局進行設計。例如,當容器或視口寬度較小時,我們需要每行顯示1個項。
當有3個或更少的項時,間距是水平的,而當有5個或更多時,間距是垂直的。我們可以通過將頁邊距從水平方向翻轉到垂直方向,或者通過使用CSS gap與Flexbox來手動管理。但是,在這種情況下,我們又不得不使用inline-flex。
CSS :nth-last-child偽類是構建條件性布局的關鍵。通過將它與CSS :has選擇器相結合,我們可以檢查一個父元素是否至少有特定數量的項,并對其進行相應的樣式設計。這種可能性是無窮無盡的!
圖片
當我們需要基于子項數量而更改gird布局時,這在目前的CSS中是不可能的。在CSS的grid中,我們可以使用minmax()基于可用空間來動態改變grid。
下面是我對CSS網格minmax()的看法:
.list { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem;}
結果看起來是這樣:
圖片
這一點都不完美。我們沒有太多的控制,因為我們需要調整minmax()中的150px的值。當有4個或更少的項時,它可以很好地工作,而當有5個或更多的項時就會出現問題。
解決辦法是什么?我們可以用CSS :has檢查是否有超過5個項目或更多,并在此基礎上改變minmax()的值。
/* default grid */.list { --item-size: 200px; display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--item-size), 1fr)); gap: 1rem;}/* If the grid has 5+ items, change the --item-size width to 150px */.list:has(li:nth-last-child(n + 5)) { --item-size: 150px;}
我只是改變了--item-size變量,使代碼更容易閱讀,并避免重復。
在下圖中,我們有一個標題,當導航項有4個或更多時,應該改變其布局。通過CSS :has和:nth-last-child,我們可以檢測并改變布局。
圖片
.site-header:has(li:nth-last-child(n + 4)) { .site-header__wrapper > * { flex: initial; } .site-header__start { order: 2; } .site-header__middle { order: -1; text-align: start; } .site-header__end { margin-left: auto; }}
以上是Sass的代碼。如果用CSS寫,可能看起來有點多。
.site-header:has(li:nth-last-child(n + 4)) .site-header__wrapper > * { flex: initial;}.site-header:has(li:nth-last-child(n + 4)) .site-header__start { order: 2;}.site-header:has(li:nth-last-child(n + 4)) .site-header__middle { order: -1; text-align: start;}.site-header:has(li:nth-last-child(n + 4)) .site-header__end { margin-left: auto;}
我們能做得更好嗎?可以。但這還沒有得到很好的支持(目前來說)。我們可以添加一個布爾CSS變量,當標題有4個或更多的項目時,它將被切換,然后使用樣式查詢來改變標題。
.site-header:has(li:nth-last-child(n + 4)) { --layout-2: true;}
有了這個,當導航項有4個或更多時,我們設置變量--layout-2。
/* This will only works if the --layout-2 CSS variable is set */@container style(--layout-2: true) { .site-header__wrapper { > * { flex: initial; } } .site-header__start { order: 2; } .site-header__middle { order: -1; text-align: start; } .site-header__end { margin-left: auto; }}
下面是一個新聞部分的設計,當項目數為3或更多時,它應該改變其布局。
圖片
通過組合CSS的:has和:nth-last-child,我們可以創建一個切換的CSS變量,它將被一個樣式查詢所檢查。
首先,我將假設默認的卡片樣式是水平的。
<class="layout"> <article class="card"></article> <article class="card"></article> <article class="card"></article></div>
.layout { display: grid; grid-gap: 1rem;}.card { display: flex; gap: 1rem; align-items: center;}
然后,我需要檢查.card元素的數量。
.layout:has(.card:nth-last-child(n + 4)) { --layout-4: true; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));}
現在,我們有一個CSS變量--layout-4,只有當我們有4個或更多的項時才會被切換。我們可以用一個樣式查詢來檢查,并相應地更新.card的樣式。
@container style(--layout-4: true) { .card { flex-direction: column; } .card__thumb { flex: 1; aspect-ratio: 4 / 3; }}
在一個設計系統中,我們可能需要根據我們有多少個操作來動態地控制模態操作的排列。
請看下圖:
圖片
比如說,如果只有一個操作,它應該居中。否則,向右對齊它們。
下面是CSS:
.modal__footer { display: flex; justify-content: center; gap: 0.5rem;}/* If there are 2 buttons or more */.modal__footer:has(a:nth-last-child(n + 2)) { justify-content: flex-end;}
很簡單,對不對。
在編輯網站上,一篇文章可能由多個作者撰寫。一個常見的模式是,當我們有多個作者時,用負間距堆疊作者的圖像。
圖片
僅僅通過使用數量查詢,我們就可以最低限度的實現,也就是:
img:nth-last-child(n+2) ~ img { border: 2px solid #fff; margin-left: -0.25rem; width: 30px; height: 30px;}
上面的方法可行,但它有局限性。如果我們想對容器本身進行樣式設計呢?那么,這就是CSS :has變得強大的地方。
首先,我們需要檢查并切換CSS變量:
.post-author:has(img:nth-last-child(n + 2)) { --multiple-avatars: true;}
如果CSS變量為true,就為多個頭像應用下面的樣式:
@container style(--multiple-avatars: true) { .avatars-list { display: flex; background-color: #efefef; padding: 8px 12px; border-radius: 50px; } img:not(:first-child) { border: solid 2px #fff; margin-left: -0.25rem; }}
另一個有趣的例子是時間線組件,它的CSS效果很好。
圖片
在這個例子中,我想讓時間線在有4個或更多項時,從垂直列表切換到交替式。
首先,使用:nth-last-child和:has:
.timeline-wrapper:has(.timeline__item:nth-last-child(n + 4)) { --alternating: true;}
如果符合上述條件,將采用以下CSS:
@container style(--alternating: true) { /* Alternating timeline styles. */}
在這里使用樣式查詢的有用之處在于,我們可以在另一個頁面上重復使用這些樣式。它不一定非得是一個有條件的CSS。
我可能會做這樣的事情:
.timeline-wrapper--page-10 { --alternating: true;}
請不要介意.timeline-wrapper--page-10,這是個故意的隨機類名。這個CSS變量可以被分配到我們想要的任何地方,而且這個CSS開箱即用。
只要寫一次,就能在很多情況下發揮作用。
在CSS中,要處理的一個棘手問題是對齊多個標識,并確保它們都看起來不錯。通過條件性CSS,我們可以檢測logo的數量,并將其尺寸縮小一些。
圖片
ul:has(li:nth-last-child(n + 8)) img { max-width: 160px; height: 35px;}
這是我所做的有趣的文章之一。結合現代的CSS功能可以讓我們以令人興奮的新方式來構建布局,這篇文章的例子也不例外。
根據項目的數量來改變樣式可能不是一次性的用法,它可以被提取到不同的用例中。通過使用樣式查詢,我們可以只寫一次,并在任何地方重用它們。
[1]https://ishadeed.com/article/conditional-css-has-nth-last-child:https://ishadeed.com/article/conditional-css-has-nth-last-child
本文鏈接:http://www.tebozhan.com/showinfo-26-95-0.html如何正確使用:Has和:Nth-Last-Child
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 不容錯過的MSBuild技巧,必備用法詳解和實踐指南
下一篇: 一篇聊聊Go錯誤封裝機制