BEM。如同前端開發領域中幾乎所有技術一樣,使用 BEM 格式編寫 CSS 可能會引起兩極分化。但在我的 Twitter 社交圈中,它至少是更受歡迎的 CSS 方法之一。
我個人認為 BEM 很好,我認為你應該使用它。但我也能理解你為什麼可能不使用它。
無論你對 BEM 的看法如何,它都提供了一些好處,最大的好處是它有助於避免 CSS 級聯中的特異性衝突。這是因為,如果使用得當,任何以 BEM 格式編寫的選擇器都應該具有相同特異性分數 (0,1,0)。多年來,我已經為許多大型網站設計了 CSS(想想政府、大學和銀行),正是這些大型項目讓我發現 BEM 真正閃耀的地方。當您確信正在編寫或編輯的樣式不會影響網站的其他部分時,編寫 CSS 會更有趣。
實際上,在某些情況下,添加特異性被認為是完全可以接受的。例如::hover
和 :focus
偽類。它們的特異性分數為 0,2,0。另一個是偽元素——例如 ::before
和 ::after
——它們的特異性分數為 0,1,1。不過,在本文的其餘部分,讓我們假設我們不希望出現任何其他特異性蔓延。 ?
但我並不是真的想向你推銷 BEM。相反,我想談談我們如何將它與現代 CSS 選擇器(例如 :is()
、:has()
、:where()
等)結合使用,以獲得更強的級聯控制。
CSS 選擇器級別 4 規範為我們提供了一些強大且相對較新的選擇元素的方法。我最喜歡的一些包括 :is()
、:where()
和 :not()
,它們都受所有現代瀏覽器支持,並且現在幾乎可以在任何項目中安全使用。
:is()
和 :where()
基本相同,只是它們對特異性的影響不同。具體來說,:where()
的特異性分數始終為 0,0,0。是的,即使是 :where(button#widget.some-class)
也沒有特異性。同時,:is()
的特異性是其參數列表中特異性最高元素的特異性。因此,我們已經有了可以使用的兩個現代選擇器之間在級聯整理方面的區別。
功能強大的 :has()
關係偽類也正在迅速獲得瀏覽器支持(在我看來,這是自 Grid 以來 CSS 最大的新功能)。但是,在撰寫本文時,:has()
的瀏覽器支持還不夠好,還不能用於生產環境。
讓我在我的 BEM 中添加一個偽類……
<code>/* ❌ 特异性分数:0,2,0 */ .something:not(.something--special) { /* 所有 something 的样式,除了特殊的 something */ }</code>
糟糕!看到那個特異性分數了嗎?記住,使用 BEM,我們理想情況下希望我們的選擇器都具有 0,1,0 的特異性分數。為什麼 0,2,0 不好?考慮一個類似的擴展示例:
<code>.something:not(a) { color: red; } .something--special { color: blue; }</code>
即使第二個選擇器在源代碼順序中最後出現,第一個選擇器更高的特異性 (0,1,1) 也會勝出,並且 .something--special
元素的顏色將設置為紅色。也就是說,假設您的 BEM 編寫正確,並且所選元素在 HTML 中同時應用了 .something
基類和 .something--special
修飾符類。
如果使用不當,這些偽類可能會以意想不到的方式影響級聯。正是這些不一致之處可能會在以後造成麻煩,尤其是在更大更複雜的代碼庫中。
還記得我剛才說的 :where()
及其特異性為零的事實嗎?我們可以利用這一點:
<code>/* ✅ 特异性分数:0,1,0 */ .something:where(:not(.something--special)) { /* 等 */ }</code>
此選擇器的第一部分(.something
)獲得其通常的特異性分數 0,1,0。但是 :where()
——以及其中的所有內容——的特異性為 0,不會進一步增加選擇器的特異性。
那些不像我那樣關心特異性的人(公平地說,可能很多人如此)在嵌套方面一直做得很好。通過一些隨意的鍵盤敲擊,我們可能會得到這樣的 CSS(請注意,我使用 Sass 簡潔起見):
<code>.card { ... } .card--featured { /* 等 */ .card__title { ... } .card__title { ... } } .card__title { ... } .card__img { ... }</code>
在這個例子中,我們有一個 .card
組件。當它是一個“特色”卡片(使用 .card--featured
類)時,卡片的標題和圖片需要不同的樣式。但是,正如我們現在所知,上面的代碼導致的特異性分數與我們系統的其餘部分不一致。
一個頑固的特異性狂熱者可能會這樣做:
<code>.card { ... } .card--featured { ... } .card__title { ... } .card__title--featured { ... } .card__img { ... } .card__img--featured { ... }</code>
還不錯,對吧?坦率地說,這是漂亮的 CSS。
不過,HTML 也有缺點。經驗豐富的 BEM 作者可能痛苦地意識到,需要復雜的模板邏輯來有條件地將修飾符類應用於多個元素。在這個例子中,HTML 模板需要有條件地將 --featured
修飾符類添加到三個元素(.card
、.card__title
和 .card__img
),但在現實世界的例子中可能更多。有很多 if 語句。
:where()
選擇器可以幫助我們編寫更少的模板邏輯——以及更少的 BEM 類——而不會增加特異性級別。
<code>.card { ... } .card--featured { ... } .card__title { ... } :where(.card--featured) .card__title { ... } .card__img { ... } :where(.card--featured) .card__img { ... }</code>
以下是 Sass 中的相同內容(注意尾隨的與號):
<code>.card { ... } .card--featured { ... } .card__title { /* 等 */ :where(.card--featured) & { ... } } .card__img { /* 等 */ :where(.card--featured) & { ... } }</code>
您是否應該選擇這種方法而不是將修飾符類應用於各種子元素,這取決於個人喜好。但至少 :where()
現在給了我們選擇!
我們生活在一個不完美的世界裡。有時您需要處理超出您控制範圍的 HTML。例如,注入您需要設置樣式的 HTML 的第三方腳本。這些標記通常不是使用 BEM 類名編寫的,在某些情況下,這些樣式根本不使用類,而是 ID!
同樣,:where()
也可以幫我們解決這個問題。此解決方案略顯笨拙,因為我們需要引用 DOM 樹中我們知道存在的某個元素的類。
<code>/* ❌ 特异性分数:0,2,0 */ .something:not(.something--special) { /* 所有 something 的样式,除了特殊的 something */ }</code>
引用父元素感覺有點冒險和限制性。如果該父類發生更改或由於某種原因不存在怎麼辦?更好的(但可能同樣笨拙的)解決方案是改用 :is()
。記住,:is()
的特異性等於其選擇器列表中最特異的選擇器。
因此,我們可以使用 :is()
引用一個我們知道(或希望!)存在的類,而不是像上面的例子那樣使用 :where()
引用它。
<code>.something:not(a) { color: red; } .something--special { color: blue; }</code>
始終存在的 body
將幫助我們選擇 #widget
元素,並且在同一個 :is()
中存在 .dummy-class
類會使 body
選擇器具有與類相同的特異性分數 (0,1,0)… 並且使用 :where()
確保選擇器不會比這更具體。
這就是我們如何利用 :is()
和 :where()
偽類的現代特異性管理功能以及使用 BEM 格式編寫 CSS 時獲得的特異性衝突預防功能。在不久的將來,一旦 :has()
獲得 Firefox 支持(在撰寫本文時,它目前在標誌後面受支持),我們可能希望將其與 :where()
配對以撤消其特異性。
無論您是否完全採用 BEM 命名,我希望我們都能同意選擇器特異性的一致性是一件好事!
以上是用BEM和Modern CSS選擇器馴服級聯的詳細內容。更多資訊請關注PHP中文網其他相關文章!