標籤:
在不同的瀏覽器寬度下使用不同的 CSS 聲明,常見的方案是使用 media query,但這個方案不支援 IE9 以下瀏覽器。
國外比較流行的 使用者介面架構 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
一種讓 IE6/7/8 支援 media query 響應式設計的方法