一.作用
與 media query(媒體查詢)類似,feature query(特性查詢)也是一種條件樣式,僅在支援特定樣式規則的環境套用指定的一組樣式:
The @supports CSS at-rule lets you specify declarations that depend on a browser's support for one or more specific CSS features. This is called a feature query.
等等,這種能力似乎 CSS 生來就有:
為了保證新屬性和新值將來可以添在現有的屬性上,使用者代理必須忽略一份非法樣式表的某一部分,如含有未知屬性的宣告、含有非法值的宣告、含有未知 @ 關鍵字的 @ 規則等等。
P.S. 具體見 4.2 處理解析錯誤的規則
CSS 從設計之初就是容錯的,目前環境下支援的樣式規則會被正確套用,不支援的會被靜默忽略掉:
Browsers simply skip over code they don’t understand, without throwing an error.
比如經常見到的:
.card {
margin: 10px;
border: 1px solid #ddd;
box-shadow: 3px 5px 5px #eee;
}
我們知道在支援 box-shadow 的環境下會呈現陰影,使之看起來像是一張懸浮的卡片,在不支援的環境則只剩下外邊距與邊框,變成扁平的普通矩形塊,算是天然的樣式降級。這種包容能力,讓新特性的套用少了一些憂慮(大不了不支援,回到降級方案)
那麼,feature query 帶來了什麼能力?
相當於內建了友好的漸進增強機制,之前通常用 Modernizr 來做的事情,現在多了一種選擇,比如:
Override one layout method with another.
與之前的區別在於,就影響範圍而言,容錯降級只會影響套用了這些不支援樣式的元素,而特性查詢降級能夠影響一組任意元素,例如:
.card {
margin: 10px;
border: 1px solid #ddd;
}
@supports (box-shadow: 3px 5px 5px #eee) {
/* 影響其他元素,之前依靠 CSS 容錯降級是做不到的 */
body:before {
content: 'box-shadow is supported!';
background-color: green;
}
.card {
box-shadow: 3px 5px 5px #eee
}
}
P.S. 嚴格地講,容錯降級一般是宣告級的(一些宣告被忽略或套用),而特性查詢降級是規則集級的(一些規則集被忽略或套用):
宣告要麼為空,要麼由一個後面跟著冒號 (:) 和屬性值的屬性名組成,之間可以有空白字元
規則集(也叫「規則」),由後面跟著一個宣告區塊的選擇器組成
二.語法
語法層面,feature query 稱為 @supports CSS at-rule,與 media query 對比如下:
@supports (display: grid) {
div {
display: grid;
}
}
@media screen and (min-width: 900px) {
article {
padding: 1rem 3rem;
}
}
長得很像,都表示 @關鍵字 後面的條件成立時,套用 {...} 裡的樣式規則。二者都是條件分組規則( conditional group rules ):
/* 一般結構 */
@IDENTIFIER (RULE) {/* CSS block */}
/* @supports CSS at-rule */
@supports <supports-condition> {
<group-rule-body>
}
另外,條件部分支援與( and )、或( or )、非( not )邏輯運算:
@supports (display: grid) and (not (display: inline-grid))
@supports (transform-style: preserve) or (-moz-transform-style: preserve)
P.S. 僅支援屬性名-值對兒形式的條件,不支援其他形式的,比如:
@supports (@charset "utf-8") {
/* 樣式規則 */
}
三.用法
實際場景中,一般模式(最佳實踐)為:
/* 降級樣式-針對低端環境 */
@supports (前沿特性) {
/* 增強樣式-針對高端環境 */
/* 必要的話,覆蓋某些降級樣式 */
}
需要注意的是,不支援某特性並不等價於否定形式的 feature query( @supports not ):
@supports not (height: 100vh) {
/* 期望僅在不支援 vh 的環境套用這組樣式規則 */
}
無法如預期地篩選出不支援 vh 的環境,因為如果連 @supports 都不支援,整個 @ 規則都會被忽略,包括這組降級樣式。也就是說,這個判斷不可靠,會漏掉一部分(既不支援 @supports 也不支援 vh 的環境)
同樣,肯定形式的 feature query 也不是完全可靠的,例如:
@supports (height: 100vh) {
/* 期望僅在支援 vh 的環境套用這組樣式規則 */
}
在不支援 @supports 但支援 vh 的環境,就不符合預期。但這通常不足為慮,因為一般需要判斷支援性的特性比 feature query 更前沿一些
四.優雅降級與漸進增強
針對瀏覽器不一致的問題,這是兩種類似的應對策略,區別在於:
-
優雅降級:高端環境優先,妥協讓步低端環境(特效全開,低端環境進行特效降級,底線是保證可用)
-
漸進增強:低端環境優先,特殊照顧高端環境(先保證可用的底線,再考慮加特技)
Graceful degradation starts complex with a goal of providing a simple experience when needed. Progressive enhancements starts simple and then adds on to that with the desired feature-rich experience.
Modernizr
Modernizr ,一般特性檢測方案,透過 JS 來檢查執行環境是否支援指定特性:
Modernizr checks if a feature is available in the browser and returns true or false.
簡言之,Modernizr 有助於區分高端與低端環境,這樣就允許我們針對低端環境進行降級(fallback),或者打補丁(polyfill)。例如:
if (Modernizr.awesomeNewFeature) {
showOffAwesomeNewFeature();
} else {
getTheOldLameExperience();
}
作為一種 JS 方案,優勢是足夠靈活,不僅支援查詢 CSS 特性,還支援所有可以透過 JS 檢測的特性,例如:
// 媒體特徵
var query = Modernizr.mq('(min-width: 900px)');
if (query) {
// the browser window is larger than 900px
}
// DOM事件
Modernizr.hasEvent('blur') // true;
// 外掛程式特性
Modernizr.on('flash', function( result ) {
if (result) {
// the browser has flash
} else {
// the browser does not have flash
}
});
但存在幾個問題:
-
效能:需要引入額外 JS,需檢測的新特性會越來越多,體積勢必越來越大,存在效能負擔
-
擴充性:依賴第三方支援,最新的特性可能需要等待一段時間才有對應的特性檢測,相當於(更新 Modernizr 版本)手動擴充
-
易用程度:透過查表得到目標特性的名稱(如
batteryapi、flexbox等,更多名稱見 Features detected by Modernizr )後,才能檢查該特性,很不方便 -
特性粒度:以上面提��的特性名稱為最小檢測單元,並不一定合適,比如
justify-content: space-evenly可能沒有與之對應的特性名稱 -
可靠性:依靠輔助手段來檢測特性支援性,並不百分之百靠譜,比如部分實作的版本,可能無法準確區分出來
CSS Feature Query
瀏覽器內建的 CSS 特性檢測支援。是否支援某樣式規則,最清楚這件事的當然是瀏覽器自己,只是這次透過 feature query 把這種內部狀態暴露出來了而已
對比 Modernizr,有幾個優勢:
-
效能更優:純 CSS 方案,不需要 JS 參與
-
擴充性良好:作為瀏覽器的基礎能力,任意新特性發布後都立即可檢測,不需要手動擴充
-
語法自然:以樣式宣告作為查詢條件,而不需要查表獲得特性名
-
細粒度:屬性名-值對兒粒度,足夠靈活
-
可靠:支援不支援是瀏覽器自己說的,絕對靠譜
當然,缺點是只支援樣式特性查詢,對於非 CSS 特性則無能為力。所以就功能而言,Modernizr 是 CSS feature query 的超集
五.相容性
-
桌面:
Firefox、Chrome、[Safari 9+]、[Edge 12+]([IE 11-]都不支援) -
行動:
[iOS 9.0+]、[Android 4.4+]
P.S. 具體見 Can I use
行動端基本可以放心使用了,即便不支援 @supports ,也沒有實質影響(只是會忽略這組樣式,與低端環境的表現一致)
特殊的,需要注意幾點:
-
feature query 無助於識別存在 bug 的特性實作,與某些不完整的特性實作(比如不支援某種機制,但無法從屬性名/值上區分出來)
-
feature query 特性本身的相容性問題會導致某些場景不符合預期(比如支援某特性,卻由於不支援
@supports而被忽略掉了),但不會造成嚴重影響
一個典型的例子是 Safari 8 支援 flexbox,但不支援 feature query,就會出現 bad case:
Safari 8 is likely the biggest problem when it comes to Feature Queries, not Internet Explorer. There are many newer properties that Safari 8 does support — like Flexbox. You probably don’t want to block Safari 8 from these properties.
例如:
body {
background-color: red;
}
@supports (display: flex) {
body {
display: flex;
background-color: green;
}
}
在 Safari 8 下就呈現降級樣式了,雖然它支援 flexbox
六.應用場景
就應用場景而言, feature query 用來解決新特性相容性方面的憂慮,作為漸進增強的一種手段,一般用法:
/* 相容性可靠的樣式:保證可訪問性 */
@supports (/* 相容性不太可靠的新特性 */) {
/* 增強:支援的話,用更簡單、效率更高、更強大的方案 */
}
漸進增強,意味著要接受多環境下的不一致。實際上,對於陰影、圓角、動畫之類的很容易接受這種不一致(在不友好的環境去掉這些錦上添花的效果),而對於 flexbox、grid 等佈局方案,似乎很難與漸進增強聯繫起來,因為佈局通常是不可或缺的,而不只是錦上添花
漸進使用 grid 特性
Is There A CSS Grid Polyfill?
Grid does things that are pretty much impossible with older layout methods. So, in order to replicate Grid in browsers that don’t have support, you would need to do a lot of work in JavaScript.
儘管很遺憾,但 grid 佈局確實沒有 CSS polyfill。那麼,非得等到多年以後才能使用這個強大的特性嗎?
當然不是。至少有兩種選擇:
-
使用 JS polyfill,比如 FremyCompany/css-grid-polyfill
-
漸進地(僅在支援的環境)使用 grid 特性(即接受不同環境下的佈局存在差異)
JS 補丁方案沒什麼好說的,對於漸進方案,關鍵在於接受這種差異:
Websites do NOT need to look the same on every browser.
把佈局效果也當作一種增強樣式(像陰影、圓角等效果一樣),允許在低端環境展示另一種不同的降級(佈局)效果。例如:
<div class="grid">
<div class="one">One</div>
<div class="two">Two</div>
<div class="three">Three</div>
</div>
對應樣式為:
* { box-sizing: border-box; }
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: 100px;
grid-gap: 20px;
}
.grid > * {
padding: 10px;
border: 5px solid rgba(214,129,137,.5);
border-radius: 5px;
background-color: rgba(233,78,119,.5);
color: #fff;
float: left;
width: 33%;
}
@supports (display:grid) {
.grid > * {
width: auto;
}
}
(摘自 display: feature queries demo )
在支援 Grid 的環境下,呈現為漂亮的涇渭分明的 3 列等比佈局,在不支援的環境,降級到稍顯擁擠的 float 佈局:
[caption id="attachment_1785" align="alignnone" width="625"]
grid-layout-css-polyfill[/caption]
差強人意,但在不改變結構、不借助 JS 的前提下, float 方案差不多只能做到這種程度了。如果能夠接受類似差異的話,透過 feature query 漸進使用,新特性的相容問題將不再重要
檢查是否支援自訂屬性
@supports (--foo: green) {
:root {
--theme-color: gray;
}
.variable {
color: var(--theme-color);
}
}
利用 CSS 變數 ,很容易把換膚功能作為一種增強效果來提供
首字母下沉效果
在支援 initial-letter 的環境(如 Safari)很容易實作這種常見的排版效果(段落首字母下沉 4 行):
@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: .5em;
}
}
不支援的話,用常規方式來實作:
p::first-letter {
float: left;
font-size: 4em;
color: #FE742F;
font-weight: bold;
margin-right: .5em;
}
P.S. 更多案例見參考資料中 feature query 相關的,如 mix-blend-mode 等
暫無評論,快來發表你的看法吧