Media Queries之Respond.js

一、stackoverflow上面对css3-mediaqueries.js与respond.js的比较

css3-mediaqueries.js

Pros

Supports min, max and min+max mediaqueries
Supports px and em values
Reacts on window resize
Elaborates on-page CSS (style tag) and external stylesheets

Cons

Doesn't support width mediaquery
Doesn't elaborate link[media="screen and ..."] nor @imported stylesheet

respond.js

Pros

Supports min, max and min+max mediaqueries
Supports px and em values
Reacts on window resize
Elaborates external stylesheets only

Cons

Doesn't support width mediaquery
Doesn't elaborate on-page CSS, link [media="screen and ..."] nor @imported stylesheets
It may cause a javascript error when combined with jQuery on load events, to solve it you need to place the script at the end of the page

二、Respond.js源码分析

https://github.com/scottjehl/Respond

A fast & lightweight polyfill for min/max-width CSS3 Media Queries (for IE 6-8, and more)

Respond.js应用的例子,参考http://skinnyties.com

查看skinnyties.com源代码,可以看到
4-1

可以在GitHub上可以下载它的未压缩版本respond.src.js(或者将min文件format,不推荐,因为JS压缩后的变量名都是处理过的,不方便阅读),本文使用Fiddler进行本地重定向,分析源码。

1.监听resize事件,以达到改变窗口大小实时响应

function callMedia(){

    applyMedia( true );

}

if( win.addEventListener ){

    // 标准2级DOM事件模型

    win.addEventListener( "resize", callMedia, false );

}

else if( win.attachEvent ){

    // IE事件模型(6、7、8)

    win.attachEvent( "onresize", callMedia );

}

2.提取CSS文件路径

var doc = win.document,

    docElem = doc.documentElement,

    mediastyles = [],

    rules = [],

    appendedEls = [],

    parsedSheets = {},

    resizeThrottle = 30,

    head = doc.getElementsByTagName( "head" )[0] || docElem,

    base = doc.getElementsByTagName( "base" )[0],

    links = head.getElementsByTagName( "link" ),

    requestQueue = [],



    // 遍历CSS路径

    ripCSS = function(){

        for( var i = 0; i < links.length; i++ ){

            var sheet = links[ i ],

            href = sheet.href,

            media = sheet.media,

            isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";



            if( !!href && isCSS && !parsedSheets[ href ] ){

                // selectivizr exposes css through the rawCssText expando

                if (sheet.styleSheet && sheet.styleSheet.rawCssText) {

                    translate( sheet.styleSheet.rawCssText, href, media );

                    parsedSheets[ href ] = true;

                } else {

                    // 判断条件:

                    // 1.有形如http://这样的href需要判断http://后面的根目录是否等于location.host。

                    // 2.不以形如http://这样开头的href,同时不包含<base>,

                    // 所以它不支持带有<base>的相对href

                    if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) ||

                        href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){

                        requestQueue.push( {

                            href: href,

                            media: media

                        } );

                    }

                }

            }

        }

        makeRequests();

    }

3.发送ajax请求返回CSS内容

// 递归执行获得CSS文本

// 递归配合shift()可以保证最后被遍历的CSS具有最高的优先级

makeRequests = function(){

    if( requestQueue.length ){

        var thisRequest = requestQueue.shift();



        ajax( thisRequest.href, function( styles ){

            translate( styles, thisRequest.href, thisRequest.media );

            parsedSheets[ thisRequest.href ] = true;



            // 在递归函数外面包裹一层setTimeout,

            // 使得函数以异步的方式执行,防止栈溢出

            win.setTimeout(function(){ makeRequests(); },0);

        } );

    }

}

// ajax方法

ajax = function( url, callback ) {

    var req = xmlHttp();

    if (!req){

        return;

    }   

    req.open( "GET", url, true );

    req.onreadystatechange = function () {

        if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){

            return;

        }

        callback( req.responseText );

    };

    if ( req.readyState === 4 ){

        return;

    }

    req.send( null );

}

4.取出CSS中具有形如 @media screen and (max-width: 480px) 的各个块

translate = function( styles, href, media ){

    // 匹配各个@media {...}

    var qs = styles.match(  /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ),

        ql = qs && qs.length || 0;



    //try to get CSS path

    href = href.substring( 0, href.lastIndexOf( "/" ) );



    var repUrls = function( css ){

            return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" );

        },

        // useMedia=true表示<link>是否具有media属性并且

        // CSS样式中没有@media块

        useMedia = !ql && media;



    //if path exists, tack on trailing slash

    if( href.length ){ href += "/"; }   



    if( useMedia ){

        ql = 1;

    }



    for( var i = 0; i < ql; i++ ){

        var fullq, thisq, eachq, eql;



        //media attr

        if( useMedia ){

            fullq = media;

            rules.push( repUrls( styles ) );

        }

        // rules保存了每个@media块内部的样式

        else{

            fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1;

            rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );

        }



        eachq = fullq.split( "," );

        eql = eachq.length;



        for( var j = 0; j < eql; j++ ){

            thisq = eachq[ j ];

            // 所有media块

            mediastyles.push( { 

                media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all",

                // 对应media块在rules数组中的样式

                rules : rules.length - 1,

                hasquery : thisq.indexOf("(") > -1,

                minw : thisq.match( /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), 

                maxw : thisq.match( /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )

            } );

        }   

    }



    applyMedia();

}

5.将匹配的样式加入文档中 

applyMedia = function( fromResize ){

    var name = "clientWidth",

        docElemProp = docElem[ name ],

        // 取得当前页面宽度

        currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,

        styleBlocks = {},

        lastLink = links[ links.length-1 ],

        now = (new Date()).getTime();



    // 函数节流,延迟调用

    if( fromResize && lastCall && now - lastCall < resizeThrottle ){

        win.clearTimeout( resizeDefer );

        resizeDefer = win.setTimeout( applyMedia, resizeThrottle );

        return;

    }

    else {

        lastCall = now;

    }

    // 遍历所有media

    for( var i in mediastyles ){

        if( mediastyles.hasOwnProperty( i ) ){

            var thisstyle = mediastyles[ i ],

                min = thisstyle.minw,

                max = thisstyle.maxw,

                minnull = min === null,

                maxnull = max === null,

                em = "em";



            // 支持以em为单位的宽度,定义了一个getEmValue方法计算em能换算成多少px



            if( !!min ){

                min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );

            }

            if( !!max ){

                max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );

            }



            // 筛选宽度匹配的样式

            if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){

                if( !styleBlocks[ thisstyle.media ] ){

                    styleBlocks[ thisstyle.media ] = [];

                }

                styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );

            }

        }

    }



    // 删除已存在的respond样式

    for( var j in appendedEls ){

        if( appendedEls.hasOwnProperty( j ) ){

            if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){

                head.removeChild( appendedEls[ j ] );

            }

        }

    }



    // 在文档中插入respond样式

    for( var k in styleBlocks ){

        if( styleBlocks.hasOwnProperty( k ) ){

            var ss = doc.createElement( "style" ),

                css = styleBlocks[ k ].join( "\n" );



            ss.type = "text/css";   

            ss.media = k;



            // originally, ss was appended to a documentFragment and sheets were appended in bulk.

            // this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set,

            // so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one!

            head.insertBefore( ss, lastLink.nextSibling );



            if ( ss.styleSheet ){ 

                // IE下

                ss.styleSheet.cssText = css;

            }

            else {

                ss.appendChild( doc.createTextNode( css ) );

            }



            //存储在appendedEls中,下次以便跟踪删除

            appendedEls.push( ss );

        }

    }

}

三、Respond.js带来的跨域请求问题

Respond.js通过ajax请求CSS文件,所以如果CSS文件存放在CDN上面(或者子域中),那么需要引入一个代理页面实现跨域连接。

GitHub地址

引入方法:

<!-- 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>

你可能感兴趣的:(media)