天天看點

深入探讨 CSS 特性檢測 @supports 與 Modernizr

什麼是 CSS 特性檢測?我們知道,前端技術日新月異的今天,各種新技術新屬性層出不窮。在 CSS 層面亦不例外。

一些新屬性能極大提升使用者體驗以及減少工程師的工作量,并且在當下的前端氛圍下:

  • 很多實驗性功能未成為标準卻被大量使用;
  • 需要相容多終端,多浏覽器,而各浏覽器對某一新功能的實作表現的天差地别;

是以有了優雅降級和漸進增強的說法,在這種背景下,又想使用新的技術給使用者提供更好的體驗,又想做好回退機制保證低版本終端使用者的基本體驗,CSS 特性檢測就應運而生了。

CSS 特性檢測就是針對不同浏覽器終端,判斷目前浏覽器對某個特性是否支援。運用 CSS 特性檢測,我們可以在支援目前特性的浏覽器環境下使用新的技術,而不支援的則做出某些回退機制。

本文将主要介紹兩種 CSS 特性檢測的方式:

  1. @supports

  2. modernizr

及使用 javascript 進行特性檢測的原理。

CSS 

@supports

傳統的 CSS 特性檢測都是通過 javascript 實作的,但是未來,原生 CSS 即可實作。

@supports

 通過 CSS 文法來實作特性檢測,并在内部 CSS 區塊中寫入如果特性檢測通過希望實作的 CSS 語句。

文法:

@supports <supports_condition> {
    /* specific rules */
}      

舉個例子:

div {
	position: fixed;
}

@supports (position:sticky) {
    div {
        position:sticky;
    }
}      

上面的例子中,

position: sticky

 是 position 的一個新屬性,用于實作黏性布局,可以輕松實作一些以往需要 javascript 才能實作的布局(戳我了解詳情),但是目前隻有在 -webkit- 核心下才得到支援。

上面的寫法,首先定義了 div 的 

position: fixed

 ,緊接着下面一句 

@supports (position:sticky)

 則是特性檢測括号内的内容,如果目前浏覽器支援 

@supports

 文法,并且支援 

position:sticky

 文法,那麼 div 的 則會被設定為 

position:sticky

 。

我們可以看到,

@supports

 文法的核心就在于這一句:

@supports (...) { }

 ,括号内是一個 CSS 表達式,如果浏覽器判斷括号内的表達式合法,那麼接下來就會去渲染括号内的 CSS 表達式。除了這種最正常的用法,還可以配合其他幾個關鍵字:

@supports not

 && 

@supports and

@supports or

@supports not

 -- 非

not 操作符可以放在任何表達式的前面來産生一個新的表達式,新的表達式為原表達式的值的否定。看個例子:

@supports not (background: linear-gradient(90deg, red, yellow)) {
    div {
        background: red;
    }
}      

因為添加了 not 關鍵字,是以與上面第一個例子相反,這裡如果檢測到浏覽器不支援線性漸變 

background: linear-gradient(90deg, red, yellow)

 的文法,則将 div 的顔色設定為紅色 

background: red

@supports and

 -- 與

這個也好了解,多重判斷,類似 javascript 的 

&&

 運算符符。用 and 操作符連接配接兩個原始的表達式。隻有兩個原始表達式的值都為真,生成的表達式才為真,反之為假。

當然,and 可以連接配接任意多個表達式看個例子:

p {
    overflow: hidden;
    text-overflow: ellipsis;
}
@supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical) {
    p {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
    }
}      

上面同時,檢測 

@supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical)

 了三個文法,如果同時支援,則設定三個 CSS 規則。這三個文法必須同時得到浏覽器的支援,如果表達式為真,則可以用于實作多行省略效果:

Demo戳我

@supports or

 -- 或

了解了 

@supports and

,就很好了解 

@supports or

 了,與 javascript 的 

||

 運算符類似,表達式中隻要有一個為真,則生成表達式表達式為真。看例子:

@supports (background:-webkit-linear-gradient(0deg, yellow, red)) or (background:linear-gradient(90deg, yellow, red)){
    div {
        background:-webkit-linear-gradient(0deg, yellow, red);
        background:linear-gradient(90deg, yellow, red)
    }
}      

上面的例子中,隻有檢測到浏覽器支援 

background:-webkit-linear-gradient(0deg, yellow, red)

 或者(or) 

background:linear-gradient(90deg, yellow, red)

 其中一個,則給 div 元素添加漸變。

當然,關鍵字 

not

 還可以和 

and

 或者 

or

 混合使用。感興趣的可以嘗試一下。

Can i use?

相容性來看,先看看 Can i use 吧:

深入探讨 CSS 特性檢測 @supports 與 Modernizr

這仍是一個實驗中的功能,雖然大部分浏覽器都已經支援了,但是目前看來,即是在移動端想要全部相容仍需要等待一段時間。

但是我們已經可以開始使用起來了,使用 

@supports

 實作漸進增強的效果。

漸進增強(progressive enhancement):針對低版本浏覽器進行建構頁面,保證最基本的功能,然後再針對進階浏覽器進行效果、互動等改進和追加功能達到更好的使用者體驗:

CSS.supports()

談到了 

@supports

,就有必要再說說 

CSS.supports()

它是作為 

@supports

 的另一種形式出現的,我們可以使用 javascript 的方式來獲得 CSS 屬性的支援情況。

可以打開控制台,輸入 

CSS.supports

 試試:

深入探讨 CSS 特性檢測 @supports 與 Modernizr

如果沒有自己實作 CSS.supports 這個方法,輸出上述資訊,表示浏覽器是支援 

@supports

 文法的,使用如下:

CSS.supports('display', 'flex')  // true
CSS.supports('position', 'sticky')  // true      
深入探讨 CSS 特性檢測 @supports 與 Modernizr

那它有什麼用呢?如果你的頁面需要動态添加一些你不确定哪些浏覽器支援的新的屬性,那它也許會派上用場。以及,它可以配合我們下文即将要講的 modernizr 。

上面介紹了 CSS 方式的特性檢測,在以前,通常是使用 javascript 來進行特性檢測的,其中 modernizr 就是其中最為出色的佼佼者。

modernizr(戳我檢視 Github )是一個開源的 javascript 庫。有着将近 2W 的 star ,其優秀程度可見一斑。

簡單看看使用方法,假設頁面已經引用了 modernizr ,文法如下:

// Listen to a test, give it a callback
Modernizr.on('testname', function( result ) {
  if (result) {
    console.log('The test passed!');
  }
  else {
    console.log('The test failed!');
  }
});

// 或者是類似 CSS.supports()
Modernizr.testAllProps('background', 'linear-gradient(90deg, #888, #ccc)');  // true      

舉個實際的例子,假設我們希望對是否支援漸變這個樣式浏覽器下的一個 div 差別對待,有如下 CSS:

div {
    background: #aaa;
}

.linear-gradient div{
    background: linear-gradient(90deg, #888, #ccc);
}      

使用 Modernizr 進行判斷,如果支援漸變,則在根元素添加一個 

.linear-gradient

 樣式,友善示例,使用了 jquery 文法:

if (Modernizr.testAllProps('background', 'linear-gradient(90deg, #888, #ccc)')) {
    $('html').addClass('linear-gradient');
}      

當然,Modernizr 還有很多其他的功能,可以去翻翻它的 API 。

特性檢測原理

如果嫌引入整一個 Modernizr 庫太大,頁面又不支援 

@supports

 ,其實我們自己用簡單的 javascript 實作也非常友善簡單。

想要知道浏覽器支援多少 CSS 屬性,可以在調試視窗試試:

var root = document.documentElement; //HTML

for(var key in root.style) {
    console.log(key);
}      

上面圖檔截取的隻是列印出來的一小部分。如果我們要檢測某個屬性樣式是否被支援,在任意的 element.style 檢測它是否存在即可,即上面代碼示例的 

root

 可以替換成任意元素。

當然,元素可能有 

background

 屬性,但是不支援具體的 

linear-gradinet()

 屬性值。這個時候該如何檢測呢?隻需要将具體的值指派給某一進制素,再查詢這個屬性值能否被讀取。

var root = document.documentElement;

root.style.backgroundImage = 'linear-gradient(90deg, #888, #ccc)';

if(root.style.backgroundImage) {
  // 支援
} else {
  // 不支援
}      

是以上面 Modernizr 的例子裡,javascript 代碼可以改成:

var root = document.documentElement;
root.style.backgroundImage = 'linear-gradient(90deg, #888, #ccc)';

if(root.style.backgroundImage) {
  $('html').addClass('linear-gradient');
}      

當然,做這種特定屬性值判斷的時候由于有個 CSS 指派操作,是以我們選取用于判斷的元素應該是一個隐藏在頁面上的元素。

各種方式間的優劣

  • 原生的 

    @supports

     的性能肯定是最好的,而且無需引入外部 javascript ,首推這個,但是無奈相容問題,目前來看不是最好的選擇。
  • Modernizr 功能強大,相容性好,但是需要引入外部 javascript,多一個 http 請求,如果隻是進行幾個特性檢測,有點殺雞用牛刀的感覺。
  • 針對需要的特性檢測,使用 javascript 實作一個簡單的函數,再把上面用到的方法封裝一下:
/**
 * 用于簡單的 CSS 特性檢測
 * @param [String] property 需要檢測的 CSS 屬性名
 * @param [String] value 樣式的具體屬性值
 * @return [Boolean] 是否通過檢查
 */
function cssTest(property, value) {
	// 用于測試的元素,隐藏在頁面上
	var ele = document.getElementById('test-display-none');

	// 隻有一個參數的情況
	if(arguments.length === 1) {
		if(property in ele.style) {
			return true;
		}
	// 兩個參數的情況
	}else if(arguments.length === 2){
		ele.style[property] = value;

		if(ele.style[property]) {
			return true;
		}
	}

	return false;
}      

簡單測試一下:

軟體工程沒有銀彈,是以無論哪種方式,都有适合的場景,我們要做的就是掌握了解它們的原理,根據不同的場景靈活運用即可。

系列 CSS 文章彙總在我的 Github 。

到此本文結束,如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。