傳統瀏覽器目前不會被完全取代,令你難以將最新的 CSS3 或 HTML5 功能嵌入你的網站。 Modernizr 正是為解決這一難題應運而生,作為一個開源的 JavaScript 庫,Modernizr 檢測瀏覽器對 CSS3 或 HTML5 功能支持情況。 Modernizr 并非試圖添加老版本瀏覽器不支持的功能,而是令你通過創建可選風格配置修改頁面設計。 它也可以通過加載定制的腳本來模擬老版本瀏覽器不支持的功能。
Modernizr 簡單易用,但不是萬能的。 成功使用 Modernizr 很大程度上取決于你的 CSS 和 JavaScript 技能。 通過本指南,你可以了解如何為不支持多欄或投影的瀏覽器設置可選風格。 此外,你還可以了解如何讓老版本瀏覽器對使用最新 HTML5 要求(required)屬性的表單進行驗證,以及如何根據瀏覽器的功能有選擇地加載腳本文件。
Modernizr 是一個用來檢測瀏覽器功能支持情況的 JavaScript 庫。 目前,通過檢驗瀏覽器對一系列測試的處理情況,Modernizr 可以檢測18項 CSS3 功能以及40多項關于HTML5 的功能。 它比傳統檢測瀏覽器名稱(瀏覽器嗅探)的方式更為可靠。 一整套測試的執行時間僅需幾微秒。 此外,Modernizr 網站通過定制腳本只對你感興趣的元素進行檢測,從而實現效率優化。
當使用 Modernizr 檢測 CSS3 支持情況時,你無需具備任何 JavaScript 的知識。 你僅需在網頁中插入文件,它隨即根據瀏覽器的功能情況在頁面的<html>標簽上添加一組類。 相應類的名稱已經符合標準化的要求并淺顯易懂。 例如,如果瀏覽器支持 box-shadow 屬性,則需要添加相應的 boxshadow 類;否則,添加一個 no-boxshadow類即可。 你所要做的一切只不過是創建一個使用這些類的式樣表,以便為相應瀏覽器提供合適的式樣。
Modernizr 可輕松實現 JavaScript 解決方案,即人們熟知的 polyfills——它模擬HTML5 相關功能和技術,如地理定位。 然而,你的確需要對 JavaScript 有基本了解以便使用這些功能和技術。 術語polyfill 來源于一種填補裂縫的黏土的英國品牌Polyfilla(即美國人熟知的填泥料)。 這里,polyfill 用來填補瀏覽器功能上的漏洞。 有時,Modernizr 可無縫地執行這項任務。 但本質上,這只是一種修補工作,所以,不能依賴它產生無漏洞瀏覽器所實現的完全相同結果。
與其它 JavaScript 庫相同的是,Modernizr 可提供 development 和 production 版本。 與大多數庫相比,唯一的不同之處是,空格和注釋已經從 production 版本中刪除了,這樣可以減少下載量的大小。 Modernizr 采取了不同的方法。 development 版本在某種意義上可稱為是“廚房中的水槽”—它幾乎包含了一切。 production 版本只包含了你選擇的那些元素,這樣能夠顯著降低下載量。 在很多情況下,production 版本可以縮小為development版本大小的二十分之一。
當使用 Modernizr 進行測試時,我建議你下載 development 版本。 一旦你掌握了它是如何運作的以及它的能力,你就可以下載一個自定義 production 版本以便在你的網站進行部署。
本教程的示例文件包含的是 Modernizr的development 2.0.6 版本,但是我建議你從 Modernizr 站點上用最新更新的版本替代它。
注: 如果你錯誤地單擊了主導航菜單中的 Download 鏈接,那么你將看到大量的復選框出現,要求你去選擇你想要的工具。 這是為定制的 production 版本所設置。 單擊面板頂部的 Development 版本的鏈接,或者單擊瀏覽器的 Back 按鈕返回主頁并選中圖1所示的 Development 按鈕。
如前所述,Modernizr 并沒有嘗試在舊版本的瀏覽器中添加新的功能,但是它允許你按照你的風格去彌補那些缺失的功能。 為了展示這是如何運作的,示例文件中包含了一個叫做 css_support_begin.html 的頁面。如果你在新式瀏覽器中加載這個頁面,它應該看起來如圖2所示。
該頁面的風格是使用了CSS3的 column-count
和 box-shadow
屬性,以便以多欄的格式顯示文本并在圖像中添加陰影。 舊版本的瀏覽器不支持這些屬性中任何一項,因此在 Internet Explorer (IE) 7 中同一頁面看起來如圖3所示。
在IE9中,同一頁面顯示了圖像陰影,但是文本和圖3中所示的布局是一樣的。
你在嘗試彌補缺失的功能上采取什么方式取決于你的設計大綱的要求。 要嘗試使頁面在所有的瀏覽器中看起來完全一樣將要涉及大量工作,但是你可以做一些簡單的改進,比如在圖像周圍環繞文字,調整圖像與文本的左邊距,以及在圖像底部和右邊緣添加隱約的邊界,以便使它更具三維立體的感覺。
Modernizr 使用 JavaScript 檢測瀏覽器所支持的功能,但是,它并不是使用 JavaScript 動態地加載不同的樣式表,而是使用非常簡單的技術將類添加到頁面的<html>標簽。然后作為設計者由你決定使用 CSS 層疊為目標元素提供合適的樣式。例如,如果頁面支持 box-shadow
屬性,那么 Modernizr 會添加 boxshadow
類。如果不支持,那么它用 no-boxshadow
類作為替代進行添加。
由于瀏覽器忽略它們無法識別的 CSS 屬性,因此你可以放心地按照你的基本樣式規則使用 box-shadow
屬性,然而需要按照下面的格式為舊版本的瀏覽器添加單獨的 descendant selector :
.no-boxshadow img { /* styles for browsers that don't support box-shadow */}
只有不支持 box-shadow
的瀏覽器才會有 no-boxshadow
類,因此其它的瀏覽器不會應用這個樣式規則。
讓我們將 Modernizr 添加到示例頁面并檢查它添加到<html> 標簽中的類。
<!DOCTYPE HTML><html>
<!DOCTYPE HTML><html class="no-js">
Modernizr 依賴于在瀏覽器中啟用的 JavaScrip。 當它啟用時,這種類會被動態地刪除。 但是,在極少數情況下,當 JavaScrip 沒有啟用時,它依然存在于 HTML makup 中,如果必要,它允許你為這樣的訪問者創建特殊的樣式規則。
</style><script src="js/modernizr.js"></script></head>
注: 如果你使用 Insert 面板或者Insert菜單,則 Dreamweaver 會添加 type="text/javascript"
到開始的 <script> 標簽中。 這在 HTML5 中不再要求,但是留著它并不會造成危害。
注:如果你的 Dreamweaver 版本沒有 Live Code(或者你正使用不同的 HTML 編輯器),那么你可以使用大多數新式瀏覽器提供的開發工具或者 Firefox 瀏覽器提供的 Firebug 檢查生成的代碼。
如圖4所示, no-js
類已經被js
類替代,這表明 JavaScript 已經啟用。
表1列舉了 Modernizr 使用的類名稱以表明對 CSS3 的支持。 如果某個功能不支持,那么相應類的名稱用no-
作前綴。
表1. Modernizr 檢測的 CSS3 功能
CSS 功能 | Modernizr 類(屬性) |
@font-face | fontface |
::before and ::after pseudo-elements | generatedcontent |
background-size | backgroundsize |
border-image | borderimage |
border-radius | borderradius |
box-shadow | boxshadow |
CSS animations | cssanimations |
CSS 2D transformations | csstransforms |
CSS 3D transformations | csstransforms3d |
CSS transitions | csstransitions |
flexible box layout | flexbox |
gradients | cssgradients |
hsla() | hsla |
multi-column layout | csscolumns |
multiple backgrounds | multiplebgs |
opacity | opacity |
reflection | cssreflections |
rgba() | rgba |
text-shadow | textshadow |
無論在哪對特定的CSS屬性進行測試,類的名稱和屬性名稱都是一樣的,然而這要求去除任何連字號或是括號。 其它類是按照它們參考的CSS3模塊而命名。
參見表1,你可以看到Modernizr使用boxshadow
和 csscolumns
分別表明了對box-shadow
屬性和多欄布局的支持。 因此,你可以使用no-boxshadow
和 no-csscolumns
類為不支持這些功能的瀏覽器創建特殊的樣式規則。
為了保證指令簡單,我將演示只有CSS 聲明的范例。 你可以直接地將它們鍵入到Code視圖中或者使用New CSS Rule對話框。
.no-boxshadow img
創建一個新的descendant (compound) selector。 #8A8A8A
)。 由此產生的樣式規則應該如下所示:.no-boxshadow img { border-right: #8A8A8A 2px solid; border-bottom: #8A8A8A 2px solid; }
這不像半透明的陰影那樣有吸引力,但是盡管如此,它仍然使得圖像能夠從背景中略微地突起。
.no-csscolumns img
創建一個新的descendant selector。.no-csscolumns img { margin: 3px 8px 3px 0; float: left;}
.columns img
規則。兩個規則有著相同的特殊性,因此,如果它們順序顛倒,則.columns img
的10像素的左邊距可能會覆蓋你剛剛創建的新規則。你可以將.no-csscolumns img
重命名為.no-csscolumns .columns img
以便于給它更高的特殊性,但是最好是保證selector越簡單越好。(順便說一下,如果你不確定什么是特殊性,查閱Adrian Senior的文章,Understanding Specificity。它雖然發表時間很長,但是很有價值。)在這個的簡單范例中,我只用了前綴為no-
的類就為舊版本的瀏覽器創建了特殊的樣式。 但是,按照它們的能力,你不同時使用這兩個類(帶或不帶前綴)為瀏覽器創建不同的樣式是絕對沒有道理的。 例如:
.csscolumns { /* rules for browsers that support multi-column layout */}.no-csscolumns { /* rules for browsers that don't support multi-column layout */}
有時這種方法是合理的,例如,如果你想為每一級支持都創建一個完全不同的布局。 但是如果它僅僅是一個為舊版本瀏覽器提供可選擇的樣式的問題,不要忘記,瀏覽器會忽略它們無法識別的屬性。 如果你對所有的樣式都使用 Modernizr 類,那么在 JavaScript 禁用的瀏覽器中你的頁面將完全非樣式化。
Modernizr 為開始的<html>標簽添加的類名稱起著雙重目的作用。當頁面加載時,它們也是 Modernizr 對象創建的 JavaScript 屬性的名稱。表1列舉了與 CSS 有關的類和屬性的名稱。表2列舉了剩下的與 HTML5 和相關技術有關的類和屬性,例如地理位置。
表2. Modernizr 檢測的與 HTML5 有關的功能
HTML5 有關功能 | Modernizr 類(屬性) |
Application cache | applicationcache |
Audio | audio.type (ogg, mp3, wav, m4a) |
Canvas | canvas |
Canvas text | canvastext |
Drag and drop | draganddrop |
Form input attributes | input.attributeName |
Form input elements | inputtypes.elementName |
Geolocation | geolocation |
hashchange event | hashchange |
History management | history |
IndexedDB | indexeddb |
Inline SVG | inlinesvg |
Local storage | localstorage |
Messaging | postmessage |
Session storage | sessionstorage |
SMIL | smil |
SVG | svg |
SVG clip paths | svgclippaths |
Touch events | touch |
Video | video.type (ogg, webm, h264) |
WebGL | webgl |
Web sockets | websockets |
Web SQL database | websqldatabase |
Web workers | webworkers |
在大多數情況下,表1和表2列舉的所有屬性返回的都是 true
或者 false
。所以,你可以按照如下所示使用 JavaScript 對本地存儲進行測試:
if (Modernizr.localstorage) { // script to run if local storage is supported} else { // script to run if local storage is not supported}
然而,就 audio
和 video
而言,返回值是一個字符串,它表明著瀏覽器能夠處理特定類型的置信水平。 根據 HTML5 規范,空的字符串表示該類型不支持。 如果支持該類型,那么返回值是“maybe”或是“probably”。 例如:
if (Modernizr.video.h264 == "") { // h264 is not supported}
HTML5 添加了許多新的表單屬性,例如 autofocus
,當頁面第一次加載時它會自動地將光標放在某個指定的字段。 另一個有用的屬性是 required
, 如果某個必需的字段留有空白,那么它將阻止HTML5兼容的瀏覽器提交表單(參見圖6)。
這很好,但是它會給你留下一個問題:舊版本的瀏覽器該怎么辦?
一個解決方法是忽略它們,并留給服務器側的驗證功能進行最終的檢查。 如果瀏覽器無法識別 required
屬性,那么另外一個處理這種情況的用戶界面更為友好的方法是創建一個小小的腳本對必需字段進行檢查。 如下的指令顯示了在Modernizr幫助下如何進行相應的操作。
</style><script src="js/modernizr.js"></script></head>
<script>
塊,并且在頁面一加載完就創建一個事件處理程序以便于執行代碼:<script src="js/modernizr.js"></script><script>window.onload = function() { // code to execute when page loads};</script></head
autofocus
和 required
屬性的瀏覽器中模擬它們。處理autofocus
的方式很簡單:window.onload = function() { // get the form and its input elements var form = document.forms[0], inputs = form.elements; // if no autofocus, put the focus in the first field if (!Modernizr.input.autofocus) { inputs[0].focus(); } // if required not supported, emulate it}
該條件測試了 Modernizr.input.autofocus
,如果不支持 autofocus
,那么返回的值是 false
。 然而,邏輯運算符NOT(一個感嘆號)卻能使意思顛倒,因此如果不支持 autofocus
,那么該條件的求值結果為 true
,并且 inputs[0].focus()
將光標放在第一個輸入字段。
required
,那么現在添加代碼以便于檢查必需字段。 事件處理程序的完整代碼如下所示:window.onload = function() { // get the form and its input elements var form = document.forms[0], inputs = form.elements; // if no autofocus, put the focus in the first field if (!Modernizr.input.autofocus) { inputs[0].focus(); } // if required not supported, emulate it if (!Modernizr.input.required) { form.onsubmit = function() { var required = [], att, val; // loop through input elements looking for required for (var i = 0; i < inputs.length; i++) { att = inputs[i].getAttribute('required'); // if required, get the value and trim whitespace if (att != null) { val = inputs[i].value; // if the value is empty, add to required array if (val.replace(/^\s+|\s+$/g, '') == '') { required.push(inputs[i].name); } } } // show alert if required array contains any elements if (required.length > 0) { alert('The following fields are required: ' + required.join(', ')); // prevent the form from being submitted return false; } }; }}
新代碼產生了一個函數,當提交表單時它能夠遍歷所有的輸入元素,以便于找到具有 required
屬性的字段。 當它找到某個字段時,它會從值中除去開頭和結尾的空白,并且如果結果是一個空的字符串,那么它會把結果添加到 required
數組中。 在所有的字段都已經得到檢查后,如果數組中包含某些元素,那么瀏覽器會顯示一個與缺失字段名稱有關的警告,并阻止提交表單。
注意: Safari 5.1 錯誤地報告了它支持 required
屬性,所以它在沒有驗證必需字段的情況下就提交了表單。 這是 Safari 的缺陷,然而在 Modernizr 里它是不存在的。
當你準備好對你的網站進行部署時,推薦創建一個 Modernizr 的自定義 production 版本,它只包含那些你實際需要的元素。 這可以按照你所選的功能將 Modernizr 庫的大小從 44KB 縮小到 2KB。當前選項的范圍如圖8所示。
選項可以便捷地按照如下類別進行分組:CSS3、HTML5、Misc(ellaneous) 和 Extra。 單擊前三個標題旁邊的 Toggle 按鈕可任意地選擇或者放棄選擇分類中的所有復選框。
在默認情形下,Extra 分類將會選中如下三個條目:
如果你在 CSS3 分類中選中任何選項,那么如下 Extra 分類中的選項也會被選中:
Modernizr.testProp()
Modernizr.testAllProps()
Modernizr._domPrefixes()
不要取消選擇這些選項。 這樣做將會自動地取消你在 CSS3 分類中已經選擇的任何選項。
Extra 分類中的MQ Polyfill (respond.js) 添加了一個腳本,它可以使IE 6-8中的 media queries 獲得有限的支持。當你選中這個選項時,它會自動地選中Media Queries 和 Modernizr.testStyles()。如需知道更多關于media queries polyfill (respond.js)的信息,請訪問https://github.com/scottjehl/Respond。
只有高級用戶才會對 Extra 分類中的其它選項感興趣。關于它們是什么和如何使用它們的更多細節,參見 Modernizr 文檔的擴展性部分(Extensibility section of the Modernizr documentation)。
下面的說明描述了如何為范例文件創建一個 Modernizr 的自定義production版本。 后續的練習需要使用這一自定義版本,它將展示如何使用 Modernizr.load()
加載外部 JavaScript 文件。
當創建Modernizr的自定義production版本時,在默認情形下,必須選中包含 Modernizr.load()
的選項。 Modernizr.load()
是 yepnope()
的別名,它是與 Modernizr 同步開發的獨立腳本加載器。 為了說明如何使用它,我給出一個簡單范例。我已經將相應的腳本從 required.html 移到了 check_required.js,并且做了三個微小的改動以便于去除 Modernizr 測試以及將它賦值到一個名為 init
的變量中。 修訂的腳本如下所示:
var init = function() { // get the form and its input elements var form = document.forms[0], inputs = form.elements; // put the focus in the first input field inputs[0].focus(); // check required fields when the form is submitted form.onsubmit = function() { var required = [], att, val; // loop through input elements looking for required for (var i = 0; i < inputs.length; i++) { att = inputs[i].getAttribute('required'); // if required, get the value and trim whitespace if (att != null) { val = inputs[i].value; // if the value is empty, add to required array if (val.replace(/^\s+|\s+$/g, '') == '') { required.push(inputs[i].name); } } } // show alert if required array contains any elements if (required.length > 0) { alert('The following fields are required: ' + required.join(', ')); // prevent the form from being submitted return false; } };};
Modernizr.load()
的一個很大的優點是,根據測試瀏覽器能力的結果,它可以有條件地加載腳本—這就是為什么起初叫它 yepnope()
的原因。 它可以異步地加載外部腳本—換句話說,就是能夠在瀏覽器已加載 Document Object Model (DOM) 之后加載外部腳本—因此它可以有助于提升你的網站性能。
Modernizr.load()
的基本語法是將一個具有如下屬性的對象傳遞給它:
test
: 你希望檢測的 Modernizr 屬性。 yep
: 如果測試成功,你希望加載的腳本的位置。 使用一個多腳本數組。 nope
: 如果測試失敗,你希望加載的腳本的位置。 使用一個多腳本數組。 complete
: 外部腳本一經加載就運行的函數(可選)。 yep
和 nope
兩者都是可選的,只要你提供了其中一個即可。
為了在 check_required.js 中加載和執行腳本,需要在 modernizr.adc.js 已附著到頁面之后添加如下<script>
塊(代碼位于required_load.html 中):
<script>Modernizr.load({ test: Modernizr.input.required, nope: 'js/check_required.js', complete: function() { init(); }});</script>
這樣就與之前運行的完全一致,但是卻可以降低已支持 required
屬性的瀏覽器的下載負荷。
為了測試多種條件,你可以給 Modernizr.load()
傳遞一組對象。如需獲得更多細節信息,參見 Modernizr 文檔上的Modernizr.load() 教程。
Modernizr 是一個強大而有用的工具,但是這并不意味著你就應該使用它。 并不是在所有情形下均必須使用 Modernizr 給瀏覽器提供多種樣式。 如果你主要關注的對象是 Internet Explorer,那么考慮使用IE conditional comments。 你也可以使用CSS層疊覆蓋一些樣式。 例如,先使用hexadecimal color,然后使用 rgba()
或 hsla()
覆蓋它。 舊版本的瀏覽器會使用第一個值并且忽略第二個值。
Modernizr 真正地變成現實是當它與 polyfills 和其它 JavaScript 相結合的時候。但是記住,通常很容易創建屬于你自己的適合支持功能的測試。例如,下面就是你測試某個瀏覽器是否支持 required
屬性的全部代碼(代碼位于required_nomodernizr.html 中):
var elem = document.createElement('input');if (typeof elem.required != 'boolean') { // required is not supported}
本教程已經涵蓋了 Modernizr 的所有主要功能 如需了解關于這些功能的更多信息,請查閱相應的官方文檔,其網站地址為 http://www.modernizr.com/docs/。 此外,你還可以找到下列有用資源:
yepnope()
的深入討論,它已經在 Modernizr 中被合并為Modernizr.load()
。本產品經 Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License 許可。Adobe 提供超出該許可范圍、與本產品包含的代碼示例相關的權限。