1. 程式人生 > >Respond.js讓IE6-8支援CSS3 Media Query

Respond.js讓IE6-8支援CSS3 Media Query

Bootstrap裡面就引入了這個js檔案,從名字看出來是自適應的相容。開啟IE看了一下,效果挺好的,自適應的效果挺好的。Respond.js讓不支援CSS3 Media Query的瀏覽器包括IE6-IE8等其他瀏覽器支援查詢。

使用方式 官方demo地址:http://scottjehl.github.com/Respond/test/test.html
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會再改變一次頁面的佈局等,所以看起來有閃屏的現象)

實現思路

  • 1.把head中所有<link rel=“sheetstyle” href=“xx”/>的css路徑取出來放入陣列
  • 2.然後遍歷陣列一個個發ajax請求
  • 3.ajax回撥後僅分析response中的media query的min-width和max-width語法,分析出viewport變化區間對應相應的css塊
  • 4.頁面初始化時和window.resize時,根據當前viewport使用相應的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下測試結果如下 T10Xg1Xl0cXXXxfAUk-634-396

測試結果發現,重新整理頁面後會有明顯的閃屏(以該測試demo為例,一開始頁面背景是黑色的,這是預設css中的,跨域js執行完成後分析出media query中的該viewport尺寸下應該應用red的背景,所以又變成紅色),間隔時間為500ms以上。所以體驗不是很好,而且該場景中ajax跨域目前已經沒有更好的實現方式,500ms間隔的閃屏避免不了。

同時因為是ajax請求css,所以會因為響應式而額外產生一個請求,好在之前css請求過一遍,這次ajax請求是讀取瀏覽器快取中的,如下圖中fiddler的檢測結果中的第三個請求和第六個請求: T1IHM0XaleXXaqU36o-600-217

respond.js總結

  • 優點:壓縮後僅1k,不跨域時效能ok,只需引入respond.js通用易用
  • 缺點:僅支援media query的min-width和max-width(用於響應式夠用);支援跨域,雖然配置有點麻煩,實現跨域代價高而且有閃屏體驗欠佳。