在 CSS 中使用特徵查詢

sunshine小小倩發表於2017-07-31

在 CSS 中使用特徵查詢

CSS 中有一個你可能還沒有聽說過的工具。它很強大。它已經存在一段時間了。並且它很可能會成為你最喜歡的 CSS 新功能之一。

這就是 @supports 規則,也被稱為 Feature Queries

通過使用 @supports,你可以在 CSS 中編寫一個小測試,以檢視是否支援某個“特性”(CSS 屬性或值),並根據其返回的結果決定是否呼叫程式碼塊。例如:

    @supports (display: grid) {
       // 只有在瀏覽器支援 CSS 網格時才會執行程式碼
     }複製程式碼

如果瀏覽器支援 display: grid,那麼括號內的所有樣式都將被應用。否則將跳過所有樣式。

現在,對於特徵查詢的用途,似乎還不是很清晰。這不是一種分析瀏覽器是否正確地實現了 CSS 屬性的外部驗證,如果你正在尋找這樣的外部驗證,參考這裡。特徵查詢要求瀏覽器對是否支援某個 CSS 屬性/值進行自我報告,並根據其返回的結果決定是否呼叫程式碼塊。如果瀏覽器不正確或不完整地實現了一個特性,@supports 不會對你有幫助。如果瀏覽器誤報了 CSS 支援的情況,@supports 不會對你有幫助。這不是一個能使瀏覽器漏洞消失的魔法。

即便如此,我仍然覺得 @supports 非常有用。如果沒有 @supports 規則的幫助,我對多個 CSS 新規則的使用就會被推遲很多。

多年來,開發者都用 Modernizr 做特徵查詢,但是 Modernizr 需要 JavaScript。即使指令碼很小,Modernizr 的構建的CSS 需要 JavaScript 檔案的下載、執行並且要在應用 CSS 之前完成。涉及 JavaScript 總是比只使用 CSS 慢。如果 JavaScript 開啟失敗也就是說如果 JavaScript 不執行會發生什麼?另外,Modernizr 需要一個複雜並且許多專案無法處理的附加層。特徵查詢速度更快、更健壯、使用起來更加簡單。

你可能會注意到,特徵查詢的語法與媒體查詢非常相似。我把他們看做堂兄弟。

    @supports (display: grid) {
      main {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      }
    }複製程式碼

現在大多數情況下,CSS 中不需要這樣的測試。例如,你在寫下面程式碼的時候不用測試其支援情況:

    aside {
      border: 1px solid black;
      border-radius: 1em;
    }複製程式碼

如果瀏覽器支援 border-radius,那麼它將在 aside 上設定圓角。如果沒有,它將跳過程式碼行並繼續前進,使框的邊緣為正方形。這裡沒有理由執行測試或使用特徵查詢。CSS 就是這樣工作的。這是 architecting solid, progressively-enhanced CSS 中的一個基本原則。瀏覽器只跳過不支援的程式碼,不丟擲錯誤。

新舊瀏覽器中圓角效果截圖
新舊瀏覽器中圓角效果截圖
大多數的瀏覽器顯示 border-radius: 1em 如圖片的右邊所示。然而,Internet Explorer 6、7 和 8 不會設定圓角,顯示效果如圖片的左邊所示。看看這個例子 codepen.io/jensimmons/…
您不需要為此進行功能查詢。

那麼,你想什麼時候使用 @supports ?特徵查詢是一種將 CSS 宣告捆綁在一起的工具,以便在一定條件下作為一個組執行。當你想在新的 CSS 功能被支援的時候,將新的和舊的 CSS 混合使用,那麼請使用特徵查詢。

讓我們看一下使用 Initial Letter 屬性的示例。這個新屬性 initial-letter 告訴瀏覽器,使元素變得更大 —— 像段首大字。在這裡,一個段落中第一個詞的第一個字母被設定為四行文字的大小。非常好。但我還是想把那字母加粗,在右邊留一點空白,讓它變成一個漂亮的橙色。酷。

    p::first-letter {
         -webkit-initial-letter: 4;
         initial-letter: 4;
         color: #FE742F;
         font-weight: bold;
         margin-right: 0.5em;
      }複製程式碼

`initial-letter`這個例子在 Safari 9 下面的截圖
`initial-letter`這個例子在 Safari 9 下面的截圖

這是我們的 initial-letter 的例子在 Safari 9 下的顯示。現在讓我們看看其他瀏覽器會發生什麼…

`initial-letter` 這個例子在其他瀏覽器下面的截圖
`initial-letter` 這個例子在其他瀏覽器下面的截圖
哦,不,這在其他瀏覽器看起來非常糟糕。這是不能接受的。我們不想改變字母的顏色,或者增加一個空白,或者讓它加粗,除非它通過 initial-letter 屬性被設定的更大了一些。我們需要一種方法來測試瀏覽器是否支援 initial-letter,並且只在顏色、粗細和空白處應用更改。進入特徵查詢。

    @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;
      }
    }複製程式碼

注意,您需要測試具有屬性和值的完整字串。最初這是令我困惑的。為什麼我要測試 initial-letter: 4?值為 4 重要嗎?如果我傳入的值是 17 呢?它是否需要與後續程式碼中的值相匹配?

@supports 規則測試一個包含屬性和值的字串,因為有時候需要測試的是屬性,有時需要測試的是值。對於 initial-letter 的例子,你傳入的是什麼值並不重要。但是考慮 @supports (display: grid),你會看到兩者都是需要的。每個瀏覽器都支援 display。只有測試版瀏覽器支援 display: grid(目前來說)。

回到我們的示例:目前 initial-letter 僅在 Safari 9 中得到支援,並且它需要字首。所以我寫了這個字首,為了確保包含無字首的版本我寫了這個測試。是的,可以在特徵查詢中使用 orandnot 語句。

這是新的結果。瀏覽器支援 initial-letter 的話就會將其展現為字型更大、加粗並且是橘色的段首字母。其它瀏覽器表現的像段首字母不存在一樣,但如果這些瀏覽器支援了這個規則,那麼視覺效果將會是一樣的。(順便說一下,目前 Firefox 正在嘗試實現段首字母特性。)

使用之前和之後的對比
使用之前和之後的對比
截圖的左邊是來自 Safari 9。其它瀏覽器展現的結果顯示為右邊。你可以在 codepen.io/jensimmons/… 看到這個測試的程式碼。

組織你的程式碼

現在,您可能會嘗試使用此工具將程式碼分成兩個分支。“嘿,瀏覽器,如果你支援視口單位,執行這段程式碼,如果你不支援他們,執行另一段程式碼。”這感覺很好並且很整潔。

    @supports (height: 100vh) {
      // 使用 viewport height 的佈局
    }
    @supports not (height: 100vh) {
      // 老式瀏覽器另一種佈局
    }
    // 我們希望是這樣,但這個程式碼不是很好複製程式碼

這不是一個好主意 —— 至少現在來說。你發現是什麼問題了嗎?

然而,不是所有瀏覽器都支援特徵查詢。並且瀏覽器不支援 @supports 將會跳過這部分的全部程式碼。這不是很好。

這是不是意味著,除非 100% 的瀏覽器都支援,否則我們就不能使用特徵查詢了?不是的,我們可以,並且當今我們應該使用特徵查詢。不要像最後一個例子那樣編寫程式碼。

那怎麼做才是正確的呢?這和我們在 100% 支援媒體查詢前有相同的方法。事實上,在這個過渡時期使用特徵查詢比使用媒體查詢更容易。你只要聰明點就行了。

你希望構建你的程式碼,因為最古老的瀏覽器不支援特徵查詢或您正在測試的特性。我來教你怎麼做。
(當然,在將來的某個時候,一旦 100% 的瀏覽器有特徵查詢,我們就可以更大程度地使用 @supports not,並以這種方式組織我們的程式碼。但我們還要等很多年。

支援特徵查詢

那麼特徵查詢的支援情況如何呢?

自從 2013 年年中以來,在 Firefox、Chrome、和 Opera 就已經支援 @supports 了。它也適用於 Edge 的每一個版本。Safari 在 2015 年秋季將其在Safari 9 中支援。在任何版本的 Internet Explorer、Opera Mini、Blackberry Browser 或 UC 瀏覽器中都不支援特徵查詢。

Can I use 網站支援特徵查詢的截圖
Can I use 網站支援特徵查詢的截圖
特徵查詢的支援可以檢視:特徵查詢在 Can I Use 上的結果

您可能會認為 Internet Explore 不支援特徵查詢。實際是並不是。我馬上告訴你原因。我認為最大的障礙是 Safari 8。我們需要密切關注這兒發生的事情。

讓我們來看另一個例子。假設我們有一些想要應用的佈局程式碼,為了使操作更加合理需要使用 object-fit: cover。對於不支援 object-fit 的瀏覽器,我們希望應用不同的佈局 CSS。

Can I Use 網站中關於 Object-fit 支援的截圖
Can I Use 網站中關於 Object-fit 支援的截圖
來看一下支援情況 Object Fit 在 Can I Use 上的結果

我們開始來編寫程式碼:

    div {
      width: 300px;
      background: yellow;
      // 老佈局的一些複雜程式碼
    }
    @supports (object-fit: cover) {
      img {
        object-fit: cover;
      }
      div {
        width: auto;
        background: green;
       // 新佈局的一些其他複雜的程式碼
      }
    }複製程式碼

那麼會發生什麼呢?特徵查詢要麼支援要麼不支援,新的特性 object-fit: cover 要麼支援要麼不支援。結合這些,我們有 4 種可能性:

支援特徵查詢嗎? 支援特性嗎? 會發生什麼? 這是我們想要的嗎?
支援特徵查詢 支援問題中的特性
支援特徵查詢 不支援問題中的特性
不支援特徵查詢 不支援問題中的特性
不支援特徵查詢 支援問題中的特性

情景 1:瀏覽器支援特徵查詢,並支援問題中的特性

Firefox、Chrome、Opera 和 Safari 9 都支援 object-fit@supports,所以這個測試將執行得很好,並且這個塊內的程式碼將被應用。我們的影象將通過 object-fit: cover 被裁剪,並且我們 div 的背景將是綠色的。

情景 2:瀏覽器支援特徵查詢,並且不支援問題中的特性

Edge 不支援 object-fit,但它支援 @supports,因此該測試將執行並失敗,防止程式碼塊被應用。該影象將不會有 object-fit 應用,並且 div 有黃色的背景。

這是我們想要的。

情景 3:瀏覽器不支援特徵查詢,並且也不支援問題中的特性

這就是我們的經典剋星 Internet Explorer 出現的地方。IE 不支援 @supports,並且也不支援 object-fit。你可能認為這意味著我們不能使用特徵查詢 —— 並不是。

想一下我們想要的結果。我們想要 IE 跳過整個程式碼塊。並且確實是這樣的結果。為什麼呢?因為當它執行到 @supports 時,它無法識別這個語法,並且會跳轉到結尾。

它可能跳過程式碼“出於錯誤的原因” —— 它跳過程式碼是因為它不支援 @supports,而不是因為它不支援 object-fit,但是誰在乎呢?!我們仍然得到我們想要的結果。

同樣的事情也發生在 Android 的黑莓瀏覽器和 UC 瀏覽器上。他們不支援 object-fit@supports,所以我們都準備好了。很成功。

底線是 —— 當你在瀏覽器中使用一個不支的特徵查詢的特徵查詢時,只要讓瀏覽器不支援你正在測試的功能就好了。

仔細思考程式碼的邏輯。問問自己,當瀏覽器跳過這個程式碼時會發生什麼?如果那是你想要的,你都準備好了。

場景 4:瀏覽器不支援特徵查詢,但支援問題中的特性

問題是這第 4 個組合 —— 雖然特徵查詢所包含的測試沒有執行,但是瀏覽器確實支援該特性時,並且應該執行該程式碼。

例如,object-fit 由 Safari 7.1(Mac)和 8(Mac和iOS)支援,但這兩個瀏覽器都不支援功能查詢。這同樣適用於 Opera Mini —— 它將支援 object-fit,但不支援 @supports

會發生什麼呢?這些瀏覽器進入這個程式碼塊,但並未使用程式碼,在圖片上應用 object-fit:cover,並將這個 div 的背景設定為綠色,它跳過了整個程式碼塊,留下黃色作為背景顏色。

並且這不是我們真正想要的。

支援特徵查詢嗎? 支援特性嗎? 會發生什麼? 這是我們想要的嗎?
支援特徵查詢 支援問題中的特性 CSS 被應用 是的
支援特徵查詢 不支援問題中的特性 CSS 沒有被應用 是的
不支援特徵查詢 不支援問題中的特性 CSS 沒有被應用 是的
不支援特徵查詢 支援問題中的特性 CSS 沒有被應用 不,可能不是

當然,這取決於特定的用例。也許這是我們可以忍受的一個結果。較老的瀏覽器獲得了較老瀏覽器的體驗。網頁仍在工作。

但在大多數情況下,我們希望瀏覽器能夠使用它支援的任何特性。這就是為什麼在涉及特性查詢時,Safari 8 可能是最大的問題,而不是 Internet Explorer。Safari 8 支援許多新的特性 —— 比如 Flexbox。您可能不想阻止 Safari 8 上的這些屬性。這就是為什麼我很少在 @supports 中使用 Flexbox,或者有時候,我在程式碼中至少寫三個分支,一個使用 not。(這很快就變得複雜了,所以不在這裡解釋了)。

如果您使用的功能在舊版瀏覽器中比功能查詢支援的更好的話,那麼在編寫程式碼時要仔細考慮所有的組合。確保不要把你希望這些瀏覽器實現的功能也排除在外了。

同時,可以很容易的在 @supports 中用最新的 CSS 特性 —— 例如 CSS Grid、首字母。沒有哪個瀏覽器會在不支援特徵查詢時就支援 CSS Grid 的。我們不必擔心那個包含新特性時問題多多的第四種組合,在以後這使得功能查詢非常有用的。

所有這一切都意味著IE11 雖然仍會存在很多年,我們還是可以同時使用特徵查詢和 CSS 的最新特性。

最佳實踐

現在我們明白了為什麼我們不能像這樣編寫程式碼:

    @supports not (display: grid) {
        // 較老瀏覽器的程式碼 // 不要模仿這個例子
    }
    @supports (display: grid) {
        // 較新瀏覽器的程式碼 // 我說這真的很糟糕嗎?
    }複製程式碼

如果我們這樣做,我們將阻止舊的瀏覽器獲取他們需要的程式碼。

取而代之的是,像這樣組織你的程式碼:

    // 較老瀏覽器的回退程式碼

    @supports (display: grid) {
        // 較新瀏覽器的程式碼
        // 在需要時覆蓋上面的程式碼
    }複製程式碼

這正是我們在使用媒體查詢的同時支援舊版本 IE 的策略。這個策略就是“移動優先”這個詞的來源。

我預計 CSS Grid 將在 2017 在瀏覽器中被使用,我打賭在實現未來的佈局時我們將使用大量的特徵查詢。與 JavaScript 相比,它的麻煩要小得多,而且速度要快得多。並且 @supports 能使支援 CSS Grid 的瀏覽器做有趣的和複雜的東西,同時對不支援的瀏覽器提供佈局選項。

自 2013 年年中以來,功能查詢一直存在。隨著 Safari 10 即將釋出,我相信我們已經到了將 @supports 新增到工具箱的時候了。

關於 Jen Simmons

Jen Simmons 是在 Mozilla 的一個設計師,並且是 The Web Ahead 的主持人。她正在研究網路上平面設計的未來,並在全球會議上四處教授 CSS 佈局。

Jen Simmons 的更多文章

掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章