在 CSS 裡,有一個你可能沒有聽過的工具,但是它已經出現一段時間了,而且非常強大。也許,它會成為 CSS 中你最喜歡的東西之一。
那麼,是什麼呢?就是 @support
,也就是功能查詢。
通過 @support
,你可以在 CSS 中使用一小段的測試來檢視瀏覽器是否支援一個特定的 CSS 功能(這個功能可以是 CSS 的某種屬性或者某個屬性的某個值),然後,根據測試的結果來決定是否要應用某段樣式。比如:
1 2 3 |
@supports ( display: grid ) { // 如果瀏覽器支援 Grid,這裡面的程式碼才會執行 } |
如果瀏覽器能夠理解 display: grid
,那麼,大括號裡的程式碼都會被應用,否則,這些樣式就會被跳過。
現在,對於功能查詢是什麼,你也許還有一點疑惑。這並不是通過某種額外的驗證來分析瀏覽器是否已經確切的實現了某個 CSS 屬性。如果你需要檢視額外的驗證,可以檢視 Test the Web Forward。
功能查詢讓瀏覽器自己就能夠表現出是否支援某個 CSS 屬性或者 CSS 屬性值。然後通過這個結果來判斷是否要應用某段 CSS。如果一個瀏覽器沒有正確的(或者完全的)實現一個 CSS 屬性,那麼,@supports
就沒有什麼用了。它並不是一個能夠讓瀏覽器的 bug 消失的魔杖。
但是,我已經發現 @supports
是那麼難以置信的有幫助。比起以前沒有這個屬性的時候,@supports
能夠讓我一再的使用新的 CSS 功能。
多年以來,開發者們都在使用 Modernizr 來實現功能查詢,但是 Modernizr 需要 JavaScript。雖然這部分 JavaScript 很小,但是,CSS 結構中新增了 Modernizr 的話,在 CSS 被應用之前,就需要下載 JavaScript 然後等待執行完成。比起使用 CSS,加入了 JavaScript 總是會更慢。而且,要是 JavaScript 執行失敗了呢?另外,Modernizr 還需要一層額外複雜的、很多專案都無法理解的東西。相比之下,功能查詢更快,功能更強大,使用起來更簡單。
你也許注意到了,@supports
的寫法和媒體查詢很類似,我覺得他們可能就是堂兄弟的關係。
1 2 3 4 5 6 |
@supports ( display: grid ) { main { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); } } |
大多數時候,你其實不需要這樣的測試。比如,你可以直接這樣寫:
1 2 3 4 |
aside { border: 1px solid black; border-radius: 1em; } |
如果瀏覽器能夠理解 border-radius
,那麼,在相應的容器上就會應用圓角樣式。如果它不能理解這個屬性,那麼,就會直接跳過並繼續執行下面的語句。容器的邊緣也就保持直角了。完全沒有必要使用功能查詢或者測試,CSS 就是這樣運作的。這是屬於 CSS 中穩固設計,漸進增強的一個基本的原則。瀏覽器會直接跳過他們無法解析的語句,不會丟擲任何錯誤。
大多數瀏覽器都會應用
border-radius: 1em;
,然後展示出右邊的效果。但是,在 IE6,7,8 上你卻不能看到圓角,你看到的將是左邊的效果。可以看看這個例子:A Rounded Corner Box。
對於這個例子,沒有必須要使用功能查詢。
那麼,什麼時候才需要使用 @supports
呢?功能查詢是將 CSS 宣告繫結在一起的一個工具,以便於這些 CSS 規則能夠在某種條件下以一個組合的方式執行。當你需要混合使用 CSS 的新規則和舊規則的時候,並且,僅當 CSS 新功能被支援的時候,就可以使用功能查詢了。
譯者注:以下例子中的
initial-letter
屬性現在在所有現代瀏覽器中都不受支援,所以,以下例子中的程式碼可能都是無效的程式碼。如果下文中有提到此屬性在某某瀏覽器中受支援的話,請忽略。需要了解initial-letter
詳細的說明,可以參考initial-letter | MDN。
來看一個關於使用 initial-letter
的例子。這個屬性告訴瀏覽器要將特指的那個元素變得更大,就像一個段首大字一樣。在這裡,我們要做的就是讓段落的第一個字母的大小為四行文字那麼大。同時,我們再對它進行加粗,在它的右邊設定一些 margin,還給它設定一個高亮的橘色。OK,很不錯了。
1 2 3 4 5 6 7 |
p::first-letter { margin-right: 0.5em; color: #FE742F; font-weight: bold; -webkit-initial-letter: 4; initial-letter: 4; } |
這是在 Safari 上的效果
讓我們看看在其他瀏覽器上的效果。
好吧,簡直沒法接受。除了使用 initial-letter
來達到我們想要的效果之外,我們並不想要改變字型的 color
,margin
,和font-weight
。所以,我們需要一個方法來測試瀏覽器是否支援 initial-letter
這個屬性,然後在瀏覽器支援這個屬性的時候再應用相關的樣式。所以,使用功能查詢:
1 2 3 4 5 6 7 8 9 |
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) { p::first-letter { -webkit-initial-letter: 4; initial-letter: 4; color: #FE742F; font-weight: bold; margin-right: 0.5em; } } |
注意,測試的時候需要進行完全的測試,CSS 屬性和值都得寫上。一開始我也比較疑惑,為什麼非得測試 initial-letter: 4
呢?4
這個值很重要嗎?如果我寫成 17
呢?莫非是需要匹配我即將要應用的 CSS 中的樣式嗎?
原因是這樣的:@supports
在測試的時候,需要提供屬性和值,因為有時候測試的是屬性,有時候測試的是值。對於 initial-letter
,你輸入多少值並不重要。但是,如果是 @suports ( dislay: grid )
就不一樣了,所有瀏覽器都識別 display
,但是,並不是所有瀏覽器都識別 display: grid
。
回到我們的例子,當前的 initial-letter
只在 Safari 9 上受支援,並且需要加字首。所以,我加了字首,同時,保持著不加字首的規則,並且,我還寫了測試來測試另外的屬性。沒錯,在功能查詢中,還可以使用 and, or, not
宣告。
下面是新的結果。理解 initial-letter
的瀏覽器會顯示一個巨大加粗高亮的段首大字。其他瀏覽器的行為就像是這個段首大字不存在一樣。當然,如果更多的瀏覽器支援了這個屬性,那麼,他們的行為也將會是有一個段首大字。
組織你的程式碼
現在,也許你迫不及待的想要使用這個工具來將你的程式碼分為兩個分支,使其變得乾淨一些。“Hey,瀏覽器,如果你識別 Viewport 單位,就執行這個,否則,執行另外的”。感覺很不錯,有條理。
1 2 3 4 5 6 7 |
@supports ( height: 100vh ) { // 支援 viewport height 的樣式 } @supports not ( height: 100vh ) { // 對於舊瀏覽器的替代樣式 } // 我們希望是好的,但這是一段爛程式碼 |
這段程式碼並不好,至少當前看來是這樣的。發現問題了嗎?
問題就是,並不是所有的瀏覽器都支援功能查詢,不理解 @supports
的瀏覽器會直接跳過兩段程式碼,這也許就太糟糕了。
意思就是,除非瀏覽器百分之百支援功能查詢,否則我們就沒法使用了嗎?當然不是,我們完全可以使用功能查詢,而且應該使用功能查詢,只要不像上面那樣寫程式碼就行。
那麼,怎麼做才對呢?其實與使用媒體查詢類似,我們在瀏覽器完全支援媒體查詢之前就開始使用了不是嗎?事實上,功能查詢使用起來比媒體查詢更簡單,只要腦子放聰明一點就行了。
你想要讓你的程式碼知道瀏覽器是否支援功能查詢或者正在測試的某個功能,我來告訴你怎麼做。
當然,在未來,瀏覽器 100% 支援功能查詢的時候,我們可以大量使用
@supports not
來組織我們的程式碼。
功能查詢的支援情況
所以,@supports
現在支援度什麼樣了呢?
自從 2013 年中,@supports
就能夠在 Firefox,Chrome 和 Opera 中使用了。在 Edge 的各個版本中也受支援。Safari 在 2015 年秋季才實現這個功能。具體的支援情況,請看下面這張圖:
你可能會覺得 IE 不支援此功能會是一個大問題,但是,其實不是這樣的。待會兒就會告訴你原因。我相信,最大的一個障礙是 Safari 8,我們需要留意在這個瀏覽器上發生的事情。
讓我們來看另外一個例子。假設我們有些佈局程式碼,為了正常執行,需要使用 object-fit: cover;
。對於不支援這個屬性的瀏覽器,我們想要使用不同的樣式。
所以,我們可以這樣寫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) { p::first-letter { -webkit-initial-letter: 4; initial-letter: 4; color: #FE742F; font-weight: bold; margin-right: 0.5em; } } div { width: 300px; background: yellow; } @supports (object-fit: cover) { img { object-fit: cover; } div { width: auto; background: green; } } |
會發生什麼呢?@supports
有支援或者不支援的情況,object-fit
也有支援或者不支援的情況,所以,就有了四種可能性:
功能查詢支援情況 | 屬性(或者值)支援情況 | 會發生什麼 | 是否我們想要的 |
---|---|---|---|
支援 | 不支援 | CSS 將會被應用 | 是 |
支援 | 不支援 | CSS 不會被應用 | 是 |
不支援 | 支援 | CSS 不會被應用 | 是 |
不支援 | 不支援 | CSS 不會被應用 | 否 |
最佳實踐
所以,我們應該怎麼寫功能查詢的程式碼呢?像下面這樣:
1 2 3 4 5 6 |
// fallback code for older browsers @supports ( display: grid ) { // code for newer browsers // including overrides of the code above, if needed } |
譯者注:本文的主要內容是介紹功能查詢和
@supports
的使用方法,所以,某些程式碼可能是無法執行的,希望讀者們注意。同時,由於原文中的一些內容顯得比較冗餘,所以部分內容並沒有翻譯。如果需要了解詳細內容,請檢視原文。