• 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- 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>
  • 相关阅读:
    深入理解类的填充规则
    深入理解C++虚函数表
    Java接入图灵机器人,实现与机器人聊天
    flex弹性布局学习笔记
    gulp插件gulp-usemin简单使用
    js基础知识温习:Javascript中如何模拟私有方法
    一次体验很不爽的面试经历
    JavaScript中in操作符(for..in)、Object.keys()和Object.getOwnPropertyNames()的区别
    js基础知识温习:构造函数与原型
    js基础知识温习:js中的对象
  • 原文地址:https://www.cnblogs.com/zhaodongyu/p/3253584.html
Copyright © 2020-2023  润新知