在不同的瀏覽器寬度下使用不同的 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
文章列表