文章出處

在不同的瀏覽器寬度下使用不同的 CSS 聲明,常見的方案是使用 media query,但這個方案不支持 IE9 以下瀏覽器。

國外比較流行的 UI 框架 bootstrap v3 版本中使用 media query 技術實現了柵格布局 ,但要兼容 IE8 的話,( IE6/7 沒有中國占比那么高,所以不用兼容)需要引入 Respond.js 的方案。

 

該方案的原理分以下 4 步:

1、在樣式 link 之后,載入 respond.js ,該腳本會獲取在他之前出現的 link 節點到一個數組

2、發送 ajax 請求重新獲取 link 數組中 css 文件文本內容

3、通過分析文本內容中 @media 類聲明,重新計算并應用相關樣式

4、在 window.resize 時,觸發第 3 步邏輯

 

這里的問題點有兩個:

1、第 2 步是否會造成重復的請求消耗?

2、如果 css 靜態資源存放域名與當前頁面不同,勢必會遇到 js 同源策略的限制,如何突破跨域問題?

 

問題 1 比較好回答,基本包括 IE 在內的所有瀏覽器都會對 GET 請求進行緩存,由于在第 1 步的時候瀏覽器已經請求過所有的 CSS 文件,因此在第 2 步 ajax 的時候會直接使用本地緩存,不會造成性能損耗。但由于需引用一個 respond.proxy.gif 來 hack IE 路徑問題,可能會造成一個額外的請求損耗。

 

問題 2 確實存在,Respond.js 通過 iframe proxy file 的方案突破了同源策略,詳細的講解可參考這篇文章《Respond.js讓IE6-8支持CSS3 Media Query》或自行百度相關JS跨域知識。但在 Respond.js 場景中會造成兩個問題:

1、由于 iframe 的創建是異步的,respond.proxy.js 無法阻塞渲染,勢必會造成頁面樣式的閃動(先應用了默認樣式,第 3 步樣式分析完畢后,又重新應用一次樣式)

2、由于 iframe proxy file 在 css 資源請求完成時的事件無法主動回調(子iframe無法訪問非同源父窗口),而是通過父窗口的一個定時器不斷讀取子窗口 window.name 值來實現被動通訊,因此樣式的渲染就存在進一步的延遲。

 

如果使用場景無法接收問題 2 所帶來的負面影響(有情懷的前端工程師都會把靜態資源部署到一個獨立域名,以提升網頁性能,而且也無法接受頁面出現明顯的重繪這種體驗損失),則需要考慮其他方案(另外 Respond.js 項目作者已經有幾年沒有維護了,部分BUG還沒修復 )。本文就此問題發散出一個基于 SASS + JS 的解決方案,以供參考。思路如下:

 

1、通常屏幕的分辨率寬度是符合一定規范的,而PC端網站柵格布局的常見寬度是可以窮舉的:1024px、1280px、1440px、1600px、 1920px

2、在樣式中,針對不同的瀏覽器寬度,我們可以復寫多條樣式規則,例如 body{} 、.w1280px body{} 、.w1440px body{},達到個性化的目的

3、當 window.resize 時,動態的在 html 節點上改變預設好的寬度 className ,例如寬度 width = 1620px 時,滿足 width > 1600px && width < 1920px ,因此 html.classList.add('w1600px')

 

以上方法十分直觀,IE6/7/8 使用以上方案,其他支持 media query 的瀏覽器則使用 @media only screen and (min-width: 1620px)  的 css 樣式方案。對于樣式維護性的問題(寫一大坨 @media 和 .w1440px body 之類的相同樣式肯定不利于維護),我們通過 SASS mixin 來解決,具體代碼參考以下樣式:

 

@mixin mediaWidth($min-width: 1024px, $max-width: null) {
    $widthSet: (1024px, 1280px, 1440px, 1600px, 1920px);
    $selector: ();

    @if $max-width {
        @media only screen and (min-width: $min-width) and (max-width: $max-width){ 
            @content; 
        }
    } @else {
        @media only screen and (min-width: $min-width) {
            @content; 
        }
    }

    @each $item in $widthSet {
        @if $max-width {
            @if $item >= $min-width and $item < $max-width {
                $selector: append($selector, unquote('.w#{$item} &'), 'comma');
            }
        } @else {
            @if $item >= $min-width{
                $selector: append($selector, unquote('.w#{$item} &'), 'comma');
            }
        }
    }

    #{$selector}{
        @content;
    }

}

body{
    width: 1024px;
    background-color: red;

    @include mediaWidth(1024px) {
        width: 1024px;
        background-color: orange;
    }

    @include mediaWidth(1280px, 1440px) {
        width: 1280px;
        background-color: green;
    }

    @include mediaWidth(1600px) {
        width: 1600px;
        background-color: blue;
    }

}

 

以上 SASS 編譯之后的 CSS 為:

 

body {
    width: 1024px;
    background-color: red;
}
@media only screen and (min-width: 1024px) {
    body {
        width: 1024px;
        background-color: orange;
   }
}
.w1024px body, .w1280px body, .w1440px body, .w1600px body, .w1920px body {
    width: 1024px;
    background-color: orange;
}
@media only screen and (min-width: 1280px) and (max-width: 1440px) {
    body {
        width: 1280px;
        background-color: green;
   }
}
.w1280px body {
    width: 1280px;
    background-color: green;
}
@media only screen and (min-width: 1600px) {
    body {
        width: 1600px;
        background-color: blue;
   }
}
.w1600px body, .w1920px body {
    width: 1600px;
    background-color: blue;
}

 

問題一:如果項目中沒用到 SASS 怎么辦?

從實際項目經驗來看,SASS 的引入可以大幅提高樣式文件的維護性,而且不會對前端項目流程帶來任何影響,因為你可以直接用編輯器的編譯工具在保存文件時同步編譯出 CSS 文件,例如 sublime text 的 sass build 和 build on save 插件,更不用說 sass 命令行、compass、grunt、gulp 之類的工具了。

 

問題二:如果不會 SASS 怎么辦?

學就是了,看看這個就知道有多簡單了《SASS用法指南》。

 

以上代碼示例,參看此 demo:http://yekai.net/demo/ie-media-query.html

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()