虽说响应式设计的理想状态是,需对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