雖說響應式設計的理想狀態是,需對pc/移動各類終端進行響應;可是現實是高分辨率的pc端與手機終端屏幕相差太大,像電商這樣有大量圖片和文字信息的同時排版要求精準的頁面,設計一個同時適應高分辨率pc又適合小尺寸的手機終端是挑戰 ;同時高分辨率下pc頁面信息量巨大,對於手機端用戶是否須要,也許會形成帶寬浪費;再者手機終端和pc終端的用戶操做習慣也相差甚大,這種多圖多信息量要求精準的頁面,設計出來恐怕會是2個徹底不一樣的版本,也許各自維護更方便。因爲業務形態緣由,隨着用戶分辨率的提升,1024x768已再也不是主流,寬屏用戶比例愈來愈大,所以咱們的響應式考慮如何充分利用PC用戶設備上更多空間而設計。下圖爲淘寶用戶的屏幕分辨率和瀏覽器比例,鑑於ie8-瀏覽器目前佔比約70%,media query的ie8-兼容迫於現實仍是要作……javascript
1.link標籤方式php
<link rel="stylesheet" type="text/css" media="screen" href="sans-serif.css"> <link rel="stylesheet" type="text/css" media="print" href="serif.css">
2.css方式css
@media screen { * { font-family: sans-serif } }
媒體類型有不少種:‘aural’, ‘braille’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’, ‘tv’、‘embossed’、 ‘speech’、'3d-glasses',但最經常使用的是screen和print,對於前端們來說最經常使用的應該只有screen了。應用於全部媒體類型能夠用all,省略不寫默認就是all。media query支持不少表達式,經常使用的以下,完整的查看這裏:html
@media all and (min-width: 400px) and (max-width: 700px) { /*屏幕寬度在[400px,700]之間時,應用該css*/ } @media all and (orientation: portrait) { /*設備豎屏時*/ } @media and (min-device-width: 800px) { /*最小設備寬度爲800px時*/ }
利用media query能夠輕鬆實現不一樣屏幕寬度時切換不一樣的頁面佈局,可是很不幸ie8及如下都還不支持media query,因而開始了下面的media query兼容之旅…… 目前實現media query ie兼容的庫比較成熟的有respond.js和css3-mediaqueries-js;它們各有優劣,respond.js壓縮後1k,只實現了media query中最經常使用的min-width max-width的兼容;css3-mediaqueries-js基本實現了全部css3規範中的media query特性的兼容,因此致使壓縮有16k,測試反饋其性能遠低於respond.js;不過確實一淘首頁2次響應式設計均只需用到max-width和min-width,Modernizr 和 H5BP 也均推薦使用respond.js,下面具體看看它們的實現吧前端
1.在css中正經常使用 min/max-width media queries @media screen and (min-width: 480px){ ...styles for 480px and up go here } 2.引入respond.min.js,但要在css的後面(越早引入越好,在ie下面看到頁面閃屏的機率就越低,由於最初css會先渲染出來,若是respond.js加載得很後面,這時從新根據media query解析出來的css會再改變一次頁面的佈局等,因此看起來有閃屏的現象)
//檢測是否支持media query,檢測css是否有效的方法都差很少,建立一個元素應用該css後檢測元素寬度,而後清除該元素。 window.matchMedia = window.matchMedia || (function(doc, undefined){ var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, // fakeBody required for fakeBody = doc.createElement('body'), div = doc.createElement('div'); div.id = 'mq-test-1'; div.style.cssText = "position:absolute;top:-100em"; fakeBody.style.background = "none"; fakeBody.appendChild(div); return function(q){ div.innerHTML = ''; docElem.insertBefore(fakeBody, refNode); bool = div.offsetWidth == 42; docElem.removeChild(fakeBody); return { matches: bool, media: q }; }; })(document);
.......
if( !!href && isCSS && !parsedSheets[ href ] ){ // selectivizr exposes css through the rawCssText expando if (sheet.styleSheet && sheet.styleSheet.rawCssText) { //sheet.styleSheet.rawCssText看不懂,原來是方便selectivizr和respond.js聯用,http://selectivizr.com/tests/respond/ //selectivizr的做用是 CSS3 selectors for IE;約定將原csstext放在styleSheet的link上的擴展屬性rawCssText上;這裏若是聯用selectivizr能夠少次ajax請求 translate( sheet.styleSheet.rawCssText, href, media ); parsedSheets[ href ] = true; } else { if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) || href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ requestQueue.push( { href: href, media: media } ); } } } .......
其他的代碼就是ajax實現和translate media query的max-width min-width的邏輯了;能夠看出這裏必須依賴ajax請求css路徑才能獲得css文件中的mediaquery的內容,那ajax的跨域問題就要解決了;因爲咱們的靜態資源都是要放cdn的,respond.js也給出了跨域方法,即引入代理頁面。
//把cross-domain/respond-proxy.html 放到cdn上
//把cross-domain/respond.proxy.gif 放到當前域服務器上
<!-- Respond.js proxy on external server --> <link href="http://externalcdn.com/respond-proxy.html" id="respond-proxy" rel="respond-proxy" /> <!-- Respond.js redirect location on local server --> <link href="/path/to/respond.proxy.gif" id="respond-redirect" rel="respond-redirect" /> <!-- Respond.js proxy script on local server --> <script src="/path/to/respond.proxy.js"></script>
這裏ajax跨域實現是經過代理頁面將獲取到的css,再經過window.name通訊實現;如在respond.proxy.js中
function checkFrameName() { var cssText; try { cssText = iframe.contentWindow.name; var now = new Date().getTime(),useTime = now - initTime; alert('獲取css耗時:'+ useTime + 'ms'); } catch (e) { } if (cssText) { ……//銷燬以前用於通訊的iframe,後續回調callback callback(cssText); } else{ win.setTimeout(checkFrameName, 100); } } win.setTimeout(checkFrameName, 500);//500ms後確認內部iframe的name值是否傳遞過來,後續再更新當前viewport該用的css。
由於實現跨域代理的問題,初始化頁面時應用上所有css耗時較長,如下光測試從開始執行該js文件到css取回調用以前的耗時爲500ms-515ms之間(每次刷新結果不同),ie8下測試結果以下 java
測試結果發現,刷新頁面後會有明顯的閃屏(以該測試demo爲例,一開始頁面背景是黑色的,這是默認css中的,跨域js執行完成後分析出media query中的該viewport尺寸下應該應用red的背景,因此又變成紅色),間隔時間爲500ms以上。因此體驗不是很好,並且該場景中ajax跨域目前已經沒有更好的實現方式,500ms間隔的閃屏避免不了。python
同時由於是ajax請求css,因此會由於響應式而額外產生一個請求,好在以前css請求過一遍,此次ajax請求是讀取瀏覽器緩存中的,以下圖中fiddler的檢測結果中的第三個請求和第六個請求: css3
// prevent jumping of layout by hiding everything before painting <body> 先將html移出可視區域外 var docEl = document.documentElement; docEl.style.marginLeft = '-32767px'; // make sure it comes back after a while 異常處理,萬一獲取mediaquery css失敗,重置回來 setTimeout(function () { docEl.style.marginTop = ''; }, 20000); …… // return visibility after media queries are tested 生效後從新可見 cssHelper.addListener('cssMediaQueriesTested', function () { // force repaint in IE by changing width if (ua.ie) { docEl.style.width = '1px'; } setTimeout(function () { docEl.style.width = ''; // undo width docEl.style.marginLeft = ''; // undo hide var now = new Date().getTime(); var useTime = now - initTime; alert('media query生效時間:'+useTime+'ms'); }, 0); // remove this listener to prevent following execution cssHelper.removeListener('cssMediaQueriesTested', arguments.callee); });
其他實現和respond.js基本一致,也須要使用ajax,因此css3-media-queries.js自己不支持跨域,固然非要支持跨域也能夠,也能夠像respond.js同樣使用代理頁面跨域便可,但也會出現閃屏的現象。仍是先看看不跨域狀況下,大多數人爲何選擇respond.js,主要緣由仍是完美支持的media query特性致使壓縮後16K,下載和執行時間都遜於respond.js,下面是同域下在ie8的測試結果(耗時140ms而respond.js僅15ms):
<link rel="stylesheet" type="text/css" media="screen and (max-width: 990px)" href="respond750.css&uuot;> <link rel="stylesheet" type="text/css" media="screen and (max-width: 1200px)" href="respond990.css">
<head> <style> @media screen and (min-width: 990px) { .content { width: 990px; color: red; } } @media screen and (min-width: 1200px) { .content { width: 1200px; color: green; } } .w990 .content { width: 990px; color: red; } .w1200 .content { width: 1200px; color: green; } </style> </head> <body class="w990"> <!--[if lte IE 8]> <script>(function(){ //爲了避免出現閃屏,在body下直接切換全局class,window.resize能夠在domready後切換 var D=KISSY.DOM,w=D.viewportWidth(),b=document.body; if(w<1200){D.addClass(b,"w990")} else {D.addClass(b,"w1200")} })();</script> <![endif]--> <div class="content">content</div> </body>
全局切換class這種方式維護也是個問題,首先是js分散2處,body最上方切換全局class,domready時window.resize時切換class,同時響應式尺寸增長時,須要改變js判斷條件;再看css的維護,media query一份,加全局class一份相同的,維護須要同時修改2次,初期media query幾十行也能接受,可是後來改版media query幾百行,這樣維護成本就大大增長了,全局class和media query copy相同的代碼引入less解決,使用方法以下:
#channels { .w1200() { .etao-channels { padding: 170px 0 0 30px; li { margin-right: 25px; } } } .w990() { .etao-channels { padding: 25px 0 0 15px; li { margin-right: 8px; } } .w750() { .etao-channels { padding: 5px 0 0 5px; li { margin-right: 5px; } a { color: #333; } } } } // 這樣只需維護上面一處代碼便可 #channels > .w1200; @media (max-width: 1119px) { #channels > .w990; } @media (max-width: 989px) { #channels > .w750; } .w990 { #channels > .w990; } .w750 { #channels > .w750; }
目前一淘新首頁採用以上方法維護,支持1200px、990px、750px三個尺寸的響應,不得不認可維護成本仍是偏高,歡迎各類改進建議git
實現media query i8-兼容,respond.js在不跨域的狀況下推薦使用(只有1k且不出現閃屏,使用方便,但會多一個ajax 304請求),可是跨域時(css/js在cdn)不推薦(會出現至少500ms間隔的閃屏,且須要在cdn上引入代理頁面實現跨域);css3-mediaqueries-js不推薦使用,其更可能是mediaqueries的徹底兼容實現,但min-width和max-width便可知足響應式實現的要求。基於以上,咱們採用全局切換class的方式實現ie8-的兼容,引入less解決media query 和 兼容css的重複維護的問題。github