[譯] 瀏覽器中 CSS 支援指南

無悔銘發表於2019-03-12

摘要:當你想要使用某個特性,發現它不受支援或在不同的瀏覽器中的行為不同時,這可能會令人沮喪。在本文中,Rachel Andrew 詳細介紹了不同型別的瀏覽器支援問題,並展示了 CSS 是如何發展的,以便更容易地處理這些問題。

我們將永遠不會生活在一個每個瀏覽我們網站的人都擁有相同瀏覽器和瀏覽器版本的世界中,就像我們永遠不會生活在一個每個人都擁有相同大小的螢幕和解析度的世界中一樣。這意味著應對老版本的瀏覽器,或者不支援我們想要使用的東西的瀏覽器,是 Web 開發人員工作的一部分。即便如此,現在的情況比過去好多了。在本文中,我將介紹一下我們可能遇到的不同型別的瀏覽器支援問題。我將向你們展示一些處理這些問題的方法,並介紹將來可能就會有幫助的東西。

為什麼我們有這些差異?

即使有一個世界裡的大多數瀏覽器都基於 Chromium,它們執行的 Chromium 版本也不一定和 Google 的 Chrome 瀏覽器相同。這意味著一個基於 Chromium 的瀏覽器,比如 Vivaldi,可能比 Google Chrome 落後幾個版本。

當然,使用者並不總是隨時更新他們的瀏覽器,儘管近年來隨著大多數瀏覽器預設自動升級,這種情況有所改善。

還有一種方式是,新特性首先進入瀏覽器。這種情況下,CSS 的新特性並不是由 CSS 工作組設計的,而是一個完整的規範交由瀏覽器廠商實現。通常,只有在實驗實現發生時,才能計算出規範的所有細節。 因此,特性開發是一個迭代過程,並且要求瀏覽器在開發中實現這些規範。雖然現在的實現大多是在瀏覽器的 flag 後面,或者只在 Nightly 或預覽版中可用,但是一旦瀏覽器具有完整特性,即使沒有其他瀏覽器支援,它也可能為所有人開啟。

所有的這一切都意味我們永遠不會生活在這樣一個世界:所有桌面和手機上的特性都能同時可用。儘管我們可能很喜歡這樣的世界。如果你是一名專業的 web 開發人員,那麼你的工作就要面對這個現實。

Bug vs. 缺乏支援

關於瀏覽器支援,我們面臨如下三個問題:

  1. 特性不支援:第一個問題,也是最容易處理的問題,就是瀏覽器根本不支援這個特性。

  2. 涉及瀏覽器 “Bug”:第二種情況是,瀏覽器聲稱支援該特性,但其支援方式與其他瀏覽器支援該特性的方式不同。由於在不同瀏覽器中表現出不同的行為,我們通常稱這類問題為“瀏覽器 bug”。

  3. CSS 屬性的部分支援:瀏覽器支援某一特性的一種情況,但僅在一種環境中。這個問題也越來越普遍。

當你看到不同瀏覽器之間的差異時,理解你正在處理的問題是很有用的,因此讓我們依次看看這些問題。

1. 特性不支援

如果擬使用瀏覽器不理解的 CSS 屬性或值,瀏覽器將忽略它。無論你是使用不受支援的特性,還是虛構一個特性並嘗試使用它,瀏覽器都將忽略它。如果瀏覽器不理解這一行 CSS,它就跳過這一行,繼續做它能理解的下一件事。

CSS 的這種設計原則意味著你可以愉快地使用新特性,因為你知道如果瀏覽器不支援這些特性,也不會有什麼壞事發生。對於一些純粹用作增強的 CSS,使用該特性,確保當該特性不可用時體驗仍然良好,這就是你需要做的全部工作,僅此而已。這種方法是漸進式增強背後的基本思想,使用瀏覽器的這個特性可以在不理解新特性的瀏覽器中安全使用新特性。

如果你想檢查瀏覽器是否支援你正在使用的特性,那麼你可以檢視 Can I Use。另一個尋找詳細支援資訊的好地方是在 MDN 上的每個 CSS 屬性所在的頁面。那裡的瀏覽器支援資料往往非常詳細。

新 CSS 理解舊 CSS

隨著 CSS 新特性的開發,需要注意它們如何與現有的 CSS 相互影響。例如,在 Grid 和 Flexbox 規範中,詳細說明了 “display: Grid” 和 “display: flex” 如何處理浮動項變成網格項或 multicol 容器變成網格等場景。這意味著某些現有的行為會被忽略,從而幫助你簡單地覆蓋不支援的瀏覽器的 CSS。這些重寫的詳細資訊參見 Progressive enhancement and Grid Layout on MDN.

使用特性查詢檢測支援

上面的方法只在你要用的 CSS 不需要其他屬性的情況下才有效。你可能需要在 CSS 中為老版本瀏覽器新增額外的屬性,然後支援該特性的瀏覽器才會對這些屬性進行解釋。

在網格佈局的使用中,可以找到一個很好的例子。當浮動項變成網格項時,將失去所有浮動行為。但如果你試圖為帶有浮動的網格佈局建立回退,則你可能會為這些項新增百分比寬度和可能的邊距。

.grid > .item {
    width: 23%;
    margin: 0 1%;
}
複製程式碼

A four column layout

使用浮動,我們可以建立一個四列布局,寬度和邊距需要設定為 '%'。(Large preview)

當浮動項是網格項時,這些寬度和邊距仍然適用。寬度為網格軌道的百分比,而不是容器的寬度;然後將應用任何邊距以及你可能指定的間隙。

.grid > .item {
    width: 23%;
    margin: 0 1%;
}

.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    column-gap: 1%;
}
複製程式碼

A four column layout with squished columns

寬度現在是網格軌道的百分比,而不是容器的。 (Large preview)

幸運的是,現代瀏覽器的 CSS 中內建了一個特性可以幫助我們處理這種情況。特性查詢允許我們直接詢問瀏覽器支援什麼,然後對響應進行操作。就像媒體查詢測試裝置或螢幕的一些屬性一樣,特性查詢測試瀏覽器對 CSS 屬性和值的支援。

測試支援

測試支援是最簡單的情況,我們使用 “@supports” 測試 CSS 屬性和值。只有當瀏覽器以 true 響應時,即它確實支援該特性,特性查詢中的內容才會執行。

測試不支援

你可以詢問瀏覽器是否不支援某個特性。只有當瀏覽器表明不支援時,特性查詢中的程式碼才會執行。

@supports not (display: grid) {
    .item {
        /* CSS from browsers which do not support grid layout */
    }
}
複製程式碼

多重測試

如果需要支援多個屬性,請使用 “and”。

@supports (display: grid) and (shape-outside: circle()){
    .item {
        /* CSS from browsers which support grid and CSS shapes */
    }
}
複製程式碼

如果你需要一個或另一個屬性的支援,請使用 “or”。

@supports (display: grid) or (display: flex){
    .item {
        /* CSS from browsers which support grid or flexbox */
    }
}
複製程式碼

選擇要測試的屬性和值

你無需測試你想要使用的每個屬性,只需要測試一些能夠表明支援你計劃使用的特性的東西。如果你想使用網格佈局,你可以測試 “display: Grid”。一旦將來 subgrid 支援 進入瀏覽器,你可能需要更具體地測試 subgrid 功能。在這種情況下,你將測試 “grid-template-columns: subgrid”,並從那些支援 subgrid 的瀏覽器獲得正確的響應。

如果我們現在回到浮動的回退示例,我們可以看到特性查詢將如何為我們排序。我們需要做的是查詢一下瀏覽器的支援情況,看看它是否支援網格佈局。如果是這樣,我們可以將專案的寬度設定為 “auto”,將邊距設定為 “0”。

.grid > .item {
    width: 23%;
    margin: 0 1%;
}

@supports(display: grid) {
    .grid {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr 1fr;
        column-gap: 1%;
    }

    .grid > .item {
        width: auto;
        margin: 0;
    }
}
複製程式碼

請檢視 Rachel AndrewCodePen 上寫的筆記 Feature Queries and Grid

請注意,雖然我已經在特性查詢中包含了所有網格程式碼,但我根本無需這樣做。如果瀏覽器不理解網格屬性,它將忽略它們,這樣它們就可以安全地位於特性查詢之外。在本例中,必須包含在特性查詢中的內容是 margin 和 width 屬性,因為這些屬性對於老版本的瀏覽器程式碼是必需的,同時也可以被支援的瀏覽器所應用。

擁抱級聯

一種非常簡單的提供回退的方法是利用瀏覽器忽略它們不理解的 CSS 的事實,以及當其它所有內容都具有相同特異性的情況下,根據哪個 CSS 應用於元素來考慮源順序。

首先為不支援該特性的瀏覽器編寫 CSS。然後測試是否支援要使用的屬性,如果瀏覽器確認支援,則使用新程式碼覆蓋回退程式碼。

這與你在使用媒體查詢進行響應式設計時使用的過程大致相同,遵循的是移動優先的方法。在這種方法中,你從較小螢幕的佈局開始,然後隨著斷點的移動,為較大螢幕新增或覆蓋內容。

我可以使用 CSS 特性查詢嗎? 關於跨主要瀏覽器支援 CSS 特性查詢的資料來自 caniuse.com。

上述工作方式意味著你不需要擔心不支援特性查詢的瀏覽器。正如你從 Can I Use 中所看到的,特性查詢得到了很好的支援。不支援它們的瀏覽器是 Internet Explorer 的任何版本。

然而,你想要使用的新特性很可能在 IE 中也不受支援。因此,目前你基本上總是先為不支援的瀏覽器編寫 CSS,然後使用特性查詢進行測試。這個特性查詢應該做支援測試。

  1. 如果瀏覽器支援特性查詢,且支援正在測試的特性,將返回 true,然後將使用特性查詢中的程式碼,覆蓋老版本瀏覽器的程式碼。
  2. 如果瀏覽器支援特性查詢,但不支援正在測試的特性,則返回 false。特性查詢中的程式碼將被忽略。
  3. 如果瀏覽器不支援特性查詢,那麼特性查詢塊中的所有內容都將被忽略,這意味著像IE11這樣的瀏覽器將使用你的老版本瀏覽器程式碼,這很可能也正是你想要的!

2. 處理瀏覽器“錯誤”

值得慶幸的是,第二個瀏覽器支援問題變得不那麼常見了。如果你讀過去年年底發表的 “What We Wished For”,你就能對過去一些令人困惑的瀏覽器 bug 有一個小小的瞭解。也就是說,任何軟體都可能有 bug,瀏覽器也不例外。 如果我們加上這樣一個事實:由於規範實現的迴圈性,有時瀏覽器實現了一些東西,然後規範發生了變化,所以現在需要釋出一個更新。在更新發布之前,我們可能處於這樣一種情況,即瀏覽器之間會做一些不同的事情。

如果瀏覽器報告某些特性的支援不好,那麼特性查詢就不能幫助我們。沒有哪種模式可以讓瀏覽器說“是的,但你可能不喜歡它。”當一個實際的互操作性錯誤出現時,你可能需要在這些情況下更具創造性。

如果你認為自己看到了一個 bug,那麼首先要做的就是確認它。有時候,當我們認為自己看到了錯誤行為,瀏覽器做了不同的事情,錯誤就在我們身上。也許我們使用了一些無效的語法,或者試圖對格式不正確的 HTML 設定樣式。在這些情況下,瀏覽器會嘗試做一些事情;但是,由於你沒有按照設計的那樣使用這些語言,每種瀏覽器可能會以不同的方式處理。快速檢查 HTML 和 CSS 是否有效是非常好的第一步。

在這一點上,我可能會做一個快速搜尋,看看我的問題是否已經被廣泛理解。 有一些已知問題的倉庫,例如 FlexbugsGridbugs。 然而,即使是精心挑選的幾個關鍵字,也可能出現涵蓋相關主題的 Stack Overflow 的帖子或文章,並可能為你提供一個解決方案。

但是假設你不知道是什麼導致了這個 bug,這使得尋找解決方案相當困難。因此,下一步就是為你的問題建立一個簡化版的測試用例,即去掉任何與之無關的內容,以幫助你準確地確定觸發該 bug 的原因。如果你認為你有一個 CSS 錯誤,你可以刪除所有的 javascript,或者在框架外重新建立相同的樣式嗎?我經常使用 CodePen 來敲出我正在看到的東西的一個簡化的測試用例;這有一個額外的優勢,那就是如果我需要問問題,我可以很容易地與其他人共享程式碼。

大多數時候,一旦你孤立了這個問題,就有可能想出另一種方法來達到你想要的結果。你會發現有人想出了一個巧妙的解決辦法,或者你可以在某個地方發帖徵求意見。

這樣說來,如果你認為你有一個瀏覽器錯誤,並且找不到其他任何人談論相同的問題,那麼你很可能發現了一些應該報告的新問題。隨著最近所有新的 CSS 的釋出,在人們開始將這些新東西與 CSS 的其他部分結合使用的過程中,問題隨時可能會出現。

檢視 Lea Verou 關於報告這類問題的帖子,“Help The Community! Report Browser Bugs!”。 本文還提供了建立簡化測試用例的重要提示。

3. CSS 屬性的部分支援

由於現代 CSS 規範的設計方式,第三種型別的問題變得更加常見。如果我們考慮網格佈局和 Flexbox,這些規範都使用 Box Alignment Level 3 中的屬性和值進行對齊。因此,像 align-items, justify-content,和 column-gap 這些屬性被指定用於 Grid 和 Flexbox 和其它佈局方法一樣。

然而,在編寫本文時,gap 屬性在所有支援網格的瀏覽器中都在網格佈局中起作用,而 column-gap 屬性在 Multicol 中起作用;然而,只有 Firefox 為 Flexbox 實現了這些屬性。

如果要使用邊距為 Flexbox 建立回退,然後測試 column-gap 並刪除邊距,則在網格或多行中支援 column-gap 的瀏覽器中,框之間將沒有空間,因此回退間距也將被刪除。

@supports(column-gap: 20px) {
    .flex {
        margin: 0; /* almost everything supports column-gap so this will always remove the margins, even if we do not have gap support in flexbox. */
    }
}
複製程式碼

這是當前特性查詢的限制。我們沒有辦法測試在一個特性中對另一個特性中的支援。在上述情況下,我想問瀏覽器的是,“你是否支援 FlexBox 中的列間距?”這種情況下,我可能得到一個否定的回答,這樣我就可以使用我的回退。

CSS 碎片屬性 break-beforebreak-afterbreak-inside 也有類似的問題。當頁面被列印出來時,這些屬性有更好的支援,因此瀏覽器通常會宣告支援。然而,如果你在 multicol 中測試支援,你會得到誤報結果。我在 CSS 工作組就這個問題提出了一個問題,然而,這並不是一個容易解決的問題。如果你有什麼想法,請把它們加進去。

選擇器支援測試

目前,特性查詢只能測試 CSS 屬性和值。我們可能想要測試的另一件事是較新的選擇器的支援,例如選擇器規範的 level 4 中的選擇器。在 Firefox Nightly 的一個標誌後面有一個解釋說明和一個實現,這是一個功能查詢的新功能,它將實現這一點。

如果你在 Firefox 中訪問 about:config,並啟用標誌 layout.css.supports-selector.enabled,那麼你可以測試是否支援各種選擇器。當前的語法非常簡單,例如測試 :has 選擇器:

@supports selector(:has){
  .item {
      /* CSS for support of :has */
  }
}
複製程式碼

這是一個正在開發中的規範,不過,在我們陳述的時候,你可以看到如何新增特性來幫助我們管理始終存在的瀏覽器支援問題。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章