「這個新功能很炫,但是可以用了嗎?瀏覽器支援了嗎?」這是網頁開發一定會遭遇的問題,幸好 Firefox 與 Chrome 更新速度很快,而且常常(偷偷的)自動更新,比較沒有這些問題,但是 IE 就不是這麼一回事了,尤其是一堆舊的 IE 遲遲不願更新的情況,讓 IE 一直是網頁設計最大的挑戰,IE 真的為網頁設計帶來許多就業機會啊!
Modernizr 是什麼?
官方定義如下:「Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser.」重點如下:
- Javascript 程式
- 用來偵測
- 瀏覽器是否支援 HTML5 與 CSS3 功能
- 用來偵測使用者的瀏覽器是否支援想要使用的功能。
- 適合用來做 Progressive Enhancement。
- 在頁面載入時立即執行所有偵測(可以自訂偵測項目),並將偵測結果放在一個 Javascript 物件(Modernizr)供 Javascript 程式使用,再另外增加 CSS class 到 HTML 物件(html)上供 CSS 使用。
- 目前可以測試超過 40 個功能,而且厲害的是只需要幾毫秒的時間。
- 整合 yepnope.js 做條件式載入,有關 yepnope 請參考 yepnope.js 讓瀏覽器只載入需要的檔案 與 More in yepnope 1.5.4。
功能偵測或瀏覽器偵測?
以前習慣使用瀏覽器偵測,也就是使用navigator.userAgent來識別瀏覽器,其他作法和 Modernizr 類似,可以呼叫 javascript function,也會在 DOM 上加上 class 識別。nc.isIE = function() { var agent = navigator.userAgent.toLowerCase(); // isIE6 - /msie 6/.test(agent); // isIE7 - /msie 7/.test(agent); // isIE8 - /msie 8/.test(agent); // ... return /msie/.test(agent); }; nc.isFF = function() { var agent = navigator.userAgent.toLowerCase(); // isFF2 - /firefox\/2/.test(agent); // isFF3 - /firefox\/3/.test(agent); return /firefox/.test(agent); }; nc.isChrome = function() { var agent = navigator.userAgent.toLowerCase(); // isChrome2 - /chrome\/2/.test(agent); return /chrome/.test(agent); }; nc.browserTag = function() { if (nc.isChrome()) { $('body').addClass('chrome'); } else if (nc.isFF()) { $('body').addClass('ff'); } else if (nc.isIE()) { $('body').addClass('ie'); } }; $(function(){ nc.browserTag(); });但這樣有兩個缺點,先不管navigator.userAgent是可以竄改的,沒試過的瀏覽器就不會出現在名單上,例如 Mac 的瀏覽器、Opera、手機上的等,就算是已經試過的瀏覽器,一旦出了新版本,程式也要翻新才行,這樣實在太累了。
另外得知道哪個瀏覽器不支援哪個功能,這更是怎麼都做不好的工作。
功能偵測讓事情簡單多了,只要有辦法知道瀏覽器有沒有支援這樣的功能,就不用試過所有的瀏覽器與版本,費盡力氣去維護一份只會愈來愈長的瀏覽器名單,並且熟記哪個瀏覽器哪個版本不支援哪個功能,有了功能偵測這些都可以丟掉了。
以 IE8 以下不支援Array.indexOf為例,功能偵測只要判斷瀏覽器是否支援indexOf,然後就可以補強,而不用管是哪個瀏覽器這麼弱。
if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(obj, start) { for (var i = (start || 0), j = this.length; i < j; i++) { if (this[i] === obj) { return i; } } return -1; }; }以前雖然聽過功能偵測,但不知道要怎麼偵測,所以一直停在瀏覽器偵測的階段。
Modernizr 如何做偵測?
說穿了很簡單,就是直接建立想要偵測的 HTML5 DOM 物件,或者在一個 DOM 物件加上想要偵測的 CSS3 style,然後立即取回,沒有支援的瀏覽器只會回傳空值或者undefined。Modernizr 支援的瀏覽器
電腦上的瀏覽器:- IE6+
- Firefox 3.5+
- Opera 9.6+
- Safari 2+
- Chrome
行動裝置上的瀏覽器:
- iOS 的 Safari
- Android 的 WebKit
- Opera Mobile
- Firefox Mobile
- 以及仍在測試中的 Blackberry 6+
安裝 Modernizr
安裝?Javascript library 不都是用script加進去就好嗎?是這樣沒錯,但位置很重要。標準的作法是將script放在head裡,但後來的發展,導致script可能出現在html裡的很多地方。
如果有使用讓不支援 html5 的瀏覽器支援 html5 的 html5shiv 的話,html5shiv 一定要放在body之前,而 Modernizr 則一定要在 html5shiv 之後。
如果有用到 Modernizr 為html加上的 CSS class 的話,那 Modernizr 一定要放在head裡,否則會出現畫面跳動,從沒有 style 跳到套用 style。
<!DOCTYPE html> <html> <head> <meta charset="BIG5"> <title>RWD</title> <style type="text/css"> .m { float: left; border: 1px solid #aaa; background-color: #eee; padding: 2px; margin: 2px; } </style> <script src="jquery-1.10.2.min.js"></script> <script src="modernizr-dev-2.7.1.js"></script> <script> $(function() { var r = ''; for ( var k in Modernizr) { r += '<div class="m">' + k + '=' + Modernizr[k] + '</div>'; } $('#ModernizrJS').html(r); }); </script> </head> <body> <div id="ModernizrJS"></div> </body> </html>在 Chrome 31.0.1650.63 版可以看到 Modernizr 為html加了許多 class。
以及 Javascript 物件Modernizr裡有哪些屬性。
Polyfill
最近一直看到這個字典查不到的字,Wikipedia 的解釋:「In web development, a polyfill (or polyfiller) is downloadable code which provides facilities that are not built into a web browser. For example, many features of HTML5 are not supported by versions of Internet Explorer older than version 8 or 9, but can be used by web pages if those pages install a polyfill.[1] Web shims[2] and HTML5 Shivs are a related concepts.」歸納的重點如下:
- Javascript library
- 為舊版的瀏覽器
- 提供新版瀏覽器才有的功能
- 新功能多半是指 HTML5
- 目前常見的有 Webshims 與 html5shiv
HTML5 Cross Browser Polyfills 就是一個補強的好選擇,它幾乎提供所有 Modernizr 能偵測功能的補強。
Modernizr 提到一個有趣的觀點,有 polyfill 並不代表一定就要用它,不需要用一堆 script 把 IE7 搞的像 Chrome 一樣,除了工程師,沒有使用者會同時開兩個瀏覽器來看同一個網站,所以讓 IE7 維持它原本應有的面貌,事情會比較輕鬆也比較不會讓使用者感到困惑。
@font-face->fontface
background-size->backgroundsize
border-image->borderimage
border-radius->borderradius
box-shadow->boxshadow
Flexible Box Model->flexbox
hsla()->hsla
Multiple backgrounds->multiplebgs
opacity->opacity
rgba()->rgba
text-shadow->textshadow
CSS Animations->cssanimations
CSS Columns->csscolumns
Generated Content (:before/:after)->generatedcontent
CSS Gradients->cssgradients
CSS Reflections->cssreflections
CSS 2D Transforms->csstransforms
CSS 3D Transforms->csstransforms3d
CSS Transitions->csstransitions
這不是我來 Modernizr 的目的,看過就好。
Canvas->canvas
Canvas Text->canvastext
Drag and Drop->draganddrop
hashchange Event->hashchange
History Management->history
HTML5 Audio->audio
HTML5 Video->video
IndexedDB->indexeddb
Input Attributes->autocomplete, autofocus, list, placeholder, max, min, multiple, pattern, required, step
Input Types->search, tel, url, email, datetime, date, month, week, time, datetime-local, number, range, color
localStorage->localstorage
Cross-window Messaging->postmessage
sessionStorage->sessionstorage
Web Sockets->websockets
Web SQL Database->websqldatabase
Web Workers->webworkers
Inline SVG->inlinesvg
SMIL->smil
SVG->svg
SVG Clip paths->svgclippaths
ouch Events->touch
WebGL->webgl
Media Query 可以讓網頁依據不同的大小或者用途而套用不同的 CSS,詳細用法可以參考 讓 IE8 認識 CSS Media Query 的 Respond.js,但這種方式有一個小小的缺點,由於是寫在同一個 CSS 檔案裡,也就會下載所有 Media Query 的 CSS,即使沒用到,多半也用不到,除非使用者瀏覽到一半突然改變瀏覽器大小,但這是很少發生的情況,除了網頁設計師。
因此如果可以依據 Media Query 來下載相關的檔案,那就可以解決這問題了。
Modernizr.mq() 有一些限制:
有四種 addTest() API 可以使用。
不建議在測試名稱裡使用 dash(-),也就是上面的 track。
---
---
Modernizr.load()
偵測完要做什麼?當然是執行特定的程式,如果瀏覽器支援某功能那就執行 A 程式,不支援的話就執行 B 程式,但是如果都寫在一起,就有一點浪費頻寬下載一部分用不到的程式和瀏覽器執行效能,因為永遠只會在 A 程式與 B 程式之間擇一執行,所以 Modernizr 整合了條件式載入 yepnope.js。Modernizr.load({ test: Modernizr.geolocation, yep : 'geo.js', nope: 'geo-polyfill.js' });有關 yepnope.js 的使用,請參考 yepnope.js 讓瀏覽器只載入需要的檔案 與 More in yepnope 1.5.4。
Modernizr 偵測的 CSS 功能
箭頭左邊是功能名稱,箭頭右邊是 Modernizr 的 Javascript 物件屬性名稱或 CSS class 名稱。@font-face->fontface
background-size->backgroundsize
border-image->borderimage
border-radius->borderradius
box-shadow->boxshadow
Flexible Box Model->flexbox
hsla()->hsla
Multiple backgrounds->multiplebgs
opacity->opacity
rgba()->rgba
text-shadow->textshadow
CSS Animations->cssanimations
CSS Columns->csscolumns
Generated Content (:before/:after)->generatedcontent
CSS Gradients->cssgradients
CSS Reflections->cssreflections
CSS 2D Transforms->csstransforms
CSS 3D Transforms->csstransforms3d
CSS Transitions->csstransitions
這不是我來 Modernizr 的目的,看過就好。
Modernizr 偵測的 HTML 功能
applicationCache->applicationcacheCanvas->canvas
Canvas Text->canvastext
Drag and Drop->draganddrop
hashchange Event->hashchange
History Management->history
HTML5 Audio->audio
HTML5 Video->video
IndexedDB->indexeddb
Input Attributes->autocomplete, autofocus, list, placeholder, max, min, multiple, pattern, required, step
Input Types->search, tel, url, email, datetime, date, month, week, time, datetime-local, number, range, color
localStorage->localstorage
Cross-window Messaging->postmessage
sessionStorage->sessionstorage
Web Sockets->websockets
Web SQL Database->websqldatabase
Web Workers->webworkers
Modernizr 偵測的其他功能
Geolocation API->geolocationInline SVG->inlinesvg
SMIL->smil
SVG->svg
SVG Clip paths->svgclippaths
ouch Events->touch
WebGL->webgl
還沒找到想要的偵測?
到 Github 下載整包 Modernizr(右下角的 Download ZIP),解開後可以找到 feature-detects 目錄,裡面有多到讓人頭暈眼花的 plugin。Modernizr API
事實上前面的 Modernizr.load() 就是最常用的 Modernizr API,除了特殊狀況,很少會用到以下的 API。Modernizr.prefixed()
Modernizr.mq()
這才是我找到 Modernizr 的原因,讓 Javascript 可以執行 CSS 的 Media Query。Media Query 可以讓網頁依據不同的大小或者用途而套用不同的 CSS,詳細用法可以參考 讓 IE8 認識 CSS Media Query 的 Respond.js,但這種方式有一個小小的缺點,由於是寫在同一個 CSS 檔案裡,也就會下載所有 Media Query 的 CSS,即使沒用到,多半也用不到,除非使用者瀏覽到一半突然改變瀏覽器大小,但這是很少發生的情況,除了網頁設計師。
因此如果可以依據 Media Query 來下載相關的檔案,那就可以解決這問題了。
Modernizr.mq() 有一些限制:
- 是針對當下的瀏覽器狀況做偵測,也就是說當稍後使用者自行改變瀏覽器大小或旋轉手機方向以致 orientation 改變時,得重新呼叫 Modernizr.mq() 做偵測。
- 一定要指定 media type,可以用all。
$(window).resize(function() { Modernizr.load({ test : Modernizr.mq('only all and (max-width: 400px)'), yep : 'yep.css', nope : 'nope.css' }); }).resize();
- 如果遇到不支援 Media Query 的瀏覽器,那不管 mq 的內容為何,永遠得到 false,不過目前已經有辦法 讓 IE8 認識 CSS Media Query 的 Respond.js。
// 可以用來偵測是否支援 Media Query Modernizr.load({ test : Modernizr.mq('only all'), yep : 'yep.css', nope : 'nope.css' });
Modernizr.addTest()
如果 Modernizr 內建的測試與 Github 的 featrue-detects 都找不到想要的測試,那就自己新增吧。有四種 addTest() API 可以使用。
Modernizr.addTest(str, fn) Modernizr.addTest(str, bool) Modernizr.addTest({str: fn, str2: fn2}) Modernizr.addTest({str: bool, str2: fn})官方範例如下。
Modernizr.addTest('track', function(){ var video = document.createElement('video'); return typeof video.addTextTrack === 'function' });支援 track 的瀏覽器會得到.track的 class 與Modernizr.track為 true 的結果,不支援 track 的瀏覽器則會得到.no-track的 class 與Modernizr.track為 false 的結果。
不建議在測試名稱裡使用 dash(-),也就是上面的 track。
Modernizr.testStyles()
看不懂。Modernizr.testProp()
測試是否支援指定的 CSS style property,property 名稱必須使用駝峰式。Modernizr.testProp(str);官方範例如下。
Modernizr.testProp('pointerEvents'); // true or false
Modernizr.testAllProps()
同Modernizr.testProp(),除了指定的 property,還會測試加上瀏覽器 prefix 的 property。Modernizr.testAllProps(str);官方範例如下。
Modernizr.testAllProps('boxSizing'); // true or false
Modernizr.hasEvent()
測試指定的元件(elem)是否支援指定的事件(str)。Modernizr.hasEvent(str [,elem])官方範例如下。
Modernizr.hasEvent('gesturestart', elem); // true or false---
---
---
沒有留言:
張貼留言