zoukankan      html  css  js  c++  java
  • mass Framework css模块 v4

    CSS模块是专门用于读取或设置元素的样式,尺寸,坐标,可选择性,滚动条的模块。

    本次升级要点:

    • 把变形部分抽取出来独立成另外的模块。
    • 移除对怪异模式的支持。
    • 重构IE部分的对透明度的读写。
    • 重构IE部分的对选择性(userSelect)的设置。
    • 增加对backgroundPosition的处理。
    • 重构show, hide, toggle方法,全部调用内部的toggelDisplay方法,更方便以后的升级与重构。

    经过瘦身后,体积减少二分之一。添加大量有用链接,大家可以通过它们来拓展学习。它们也是本模块或与样式相关的其他模块的重构动力与材料。

    css模块的源码:

    //=========================================
    // 样式操作模块 v4 by 司徒正美
    //=========================================
    define( "css", !!top.getComputedStyle ? ["$node"] : ["$node","$css_fix"] , function(){
        $.log( "已加载css模块" );
        var adapter = $.cssAdapter || ($.cssAdapter = {})
        var rrelNum = /^([\-+])=([\-+.\de]+)/
        var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i
     
        adapter["_default:set"] = function( node, name, value){
            node.style[ name ] = value;
        }
        //有关单位转换的 http://heygrady.com/blog/2011/12/21/length-and-angle-unit-conversion-in-javascript/
        if ( window.getComputedStyle ) {
            adapter[ "_default:get" ] = function( node, name ) {
                var ret, width, minWidth, maxWidth, computed = window.getComputedStyle( node, null )
                if (computed ) {
                    ret = name == "filter" ? computed.getPropertyValue(name) :computed[name]
                    var style = node.style ;
                    if ( ret === "" && !$.contains( node.ownerDocument, node ) ) {
                        ret = style[name];//如果还没有加入DOM树,则取内联样式
                    }
                    //  Dean Edwards大神的hack,用于转换margin的百分比值为更有用的像素值
                    // webkit不能转换top, bottom, left, right, margin, text-indent的百分比值
                    if (  /^margin/.test( name ) && rnumnonpx.test( ret ) ) {
                        width = style.width;
                        minWidth = style.minWidth;
                        maxWidth = style.maxWidth;
    
                        style.minWidth = style.maxWidth = style.width = ret;
                        ret = computed.width;
                    
                        style.width = width;
                        style.minWidth = minWidth;
                        style.maxWidth = maxWidth;
                    }
                };
                return ret === "" ? "auto" : ret;
            }
        }
        var getter = adapter[ "_default:get" ]
    
        adapter[ "zIndex:get" ] = function( node, name, value, position ) {
            while ( node.nodeType !== 9 ) {
                //即使元素定位了,但如果zindex设置为"aaa"这样的无效值,浏览器都会返回auto;
                //如果没有指定zindex值,IE会返回数字0,其他返回auto
                position = getter(node, "position" );//getter = adapter[ "_default:get" ]
                if ( position === "absolute" || position === "relative" || position === "fixed" ) {
                    // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
                    value = parseInt( getter(node,"zIndex"), 10 );
                    if ( !isNaN( value ) && value !== 0 ) {
                        return value;
                    }
                }
                node = node.parentNode;
            }
            return 0;
        }
    
        //这里的属性不需要自行添加px
        $.cssNumber = $.oneObject("fontSizeAdjust,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom,rotate");
        $.css = function( node, name, value){
            if(node.style){//注意string经过call之后,变成String伪对象,不能简单用typeof来检测
                var prop = $.String.camelize(name)
                name = $.cssName( name ) ;
                if( value === void 0){ //获取样式
                    return (adapter[ prop+":get" ] || adapter[ "_default:get" ])( node, name );
                }else {//设置样式
                    var temp;
                    if ( typeof value === "string" && (temp = rrelNum.exec( value )) ) {
                        value =  ( temp[1] + 1) * temp[2]  + parseFloat( $.css( node, name) );
                    }
                    if ( isFinite( value ) && !$.cssNumber[ prop ] ) {
                        value += "px";
                    }
                    ;
                    (adapter[prop+":set"] || adapter[ "_default:set" ])( node, name, value );
                }
            }
        }
    
        $.fn.css =  function( name, value , neo){
            return $.access( this, name, value, $.css );
        }
    
        var cssPair = {
            ['Left', 'Right'],
            height:['Top', 'Bottom']
        }
        var cssShow = {
            position: "absolute",
            visibility: "hidden",
            display: "block"
        }
        //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
        function showHidden(node, array){
            if( node && node.nodeType == 1 && node.offsetWidth == 0 ){
                if(getter(node, "display") == "none"){
                    var obj = {
                        node: node
                    }
                    for (var name in cssShow ) {
                        obj[ name ] = node.style[ name ];
                        node.style[ name ] = cssShow[ name ];
                    }
                    array.push( obj );
                }
                showHidden(node.parentNode, array)
            }
        }
    
        var supportBoxSizing = $.cssName("box-sizing")
        adapter[ "boxSizing:get" ] = function( node, name ) {
            return  supportBoxSizing ? getter(node, name) : document.compatMode == "BackCompat" ?
            "border-box" : "content-box"
        }
    
        function setWH(node, name, val, extra){
            var which = cssPair[name]
            which.forEach(function(direction){
                if(extra < 1)
                    val -= parseFloat(getter(node, 'padding' + direction)) || 0;
                if(extra < 2)
                    val -= parseFloat(getter(node, 'border' + direction + 'Width')) || 0;
                if(extra === 3){
                    val += parseFloat(getter(node, 'margin' + direction )) || 0;
                } 
                if(extra === "padding-box"){
                    val += parseFloat(getter(node, 'padding' + direction)) || 0;
                }
                if(extra === "border-box"){
                    val += parseFloat(getter(node, 'padding' + direction)) || 0;
                    val += parseFloat(getter(node, 'border' + direction + 'Width')) || 0;
                }
            });
            return val
        }
        function getWH( node, name, extra  ) {//注意 name是首字母大写
            var hidden = [];
            showHidden( node, hidden );
            var  val = setWH(node, name,  node["offset" + name], extra);
            for(var i = 0, obj; obj = hidden[i++];){
                node = obj.node;
                for ( name in obj ) {
                    if(typeof obj[ name ] == "string"){
                        node.style[ name ] = obj[ name ];
                    }
                }
            }
            return val;
        };
        var rmapper = /(\w+)_(\w+)/g
        //生成width, height, innerWidth, innerHeight, outerWidth, outerHeight这六种原型方法
        "Height,Width".replace( $.rword, function(  name ) {
            var lower = name.toLowerCase(),
            clientProp = "client" + name,
            scrollProp = "scroll" + name,
            offsetProp = "offset" + name;
            $.cssAdapter[ lower+":get" ] = function( node ){
                return getWH( node, name, 0 ) + "px";//添加相应适配器
            }
            $.cssAdapter[ lower+":set" ] = function( node, name, value ){
                var box = $.css(node, "box-sizing");
                node.style[name] = box == "content-box" ? value:
                setWH(node, name, parseFloat(value), box ) + "px";
            }
            "inner_1,b_0,outer_2".replace(rmapper,function(a, b, num){
                var method = b == "b" ? lower : b + name;
                $.fn[ method ] = function( value ) {
                    num = b == "outer" && value === true ? 3 : num;
                    return $.access( this, num, value, function( node, num, size ) {
                        if ( $.type( node,"Window" ) ) {//取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
                            return node.documentElement[ clientProp ] ;
                        }
                        if ( node.nodeType === 9 ) {//取得页面尺寸
                            var doc = node.documentElement;
                            //FF chrome    html.scrollHeight< body.scrollHeight
                            //IE 标准模式 : html.scrollHeight> body.scrollHeight
                            //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
                            return Math.max(
                                node.body[ scrollProp ], doc[ scrollProp ],
                                node.body[ offsetProp ], doc[ offsetProp ],
                                doc[ clientProp ]
                                );
                        } else if ( size === void 0 ) {
                            return getWH( node, name, num )
                        } else {
                            return num > 0  ? this : $.css( node, lower, size );
                        }
                    }, this)
                }
            })
    
        });
    
        var sandbox,sandboxDoc;
        $.callSandbox = function(parent,callback){
            if ( !sandbox ) {
                sandbox = document.createElement( "iframe" );
                sandbox.frameBorder = sandbox.width = sandbox.height = 0;
            }
            parent.appendChild(sandbox);
            if ( !sandboxDoc || !sandbox.createElement ) {
                sandboxDoc = ( sandbox.contentWindow || sandbox.contentDocument ).document;
                sandboxDoc.write( "<!doctype html><html><body>" );
                sandboxDoc.close();
            }
            callback(sandboxDoc);
            parent.removeChild(sandbox);
        }
    
        var cacheDisplay = $.oneObject("a,abbr,b,span,strong,em,font,i,img,kbd","inline");
        var blocks = $.oneObject("div,h1,h2,h3,h4,h5,h6,section,p","block");
        $.mix(cacheDisplay ,blocks);
        function parseDisplay( nodeName ) {
            nodeName = nodeName.toLowerCase();
            if ( !cacheDisplay[ nodeName ] ) {
                $.callSandbox(document.body, function(doc){
                    var  elem = doc.createElement( nodeName );
                    doc.body.appendChild( elem );
                    cacheDisplay[ nodeName ] = getter( elem, "display" );
                });
            }
            return cacheDisplay[ nodeName ];
        }
        
        function isHidden( elem) {
            return getter( elem, "display" ) === "none" || !$.contains( elem.ownerDocument, elem );
        }
    
        function toggelDisplay( nodes, show ) {
            var elem,  values = [], status = [], index = 0, length = nodes.length;
            //由于传入的元素们可能存在包含关系,因此分开两个循环来处理,第一个循环用于取得当前值或默认值
            for ( ; index < length; index++ ) {
                elem = nodes[ index ];
                if ( !elem.style ) {
                    continue;
                }
                values[ index ] = $._data( elem, "olddisplay" );
                status[ index ] = isHidden(elem) 
                if( !values[ index ] ){
                    values[ index ] =  status[index] ? defaultDisplay(elem.nodeName): 
                    getter(elem, "display");
                    $._data( elem, "olddisplay", values[ index ])
                }
            }
            //第二个循环用于设置样式,-1为toggle, 1为show, 0为hide
            for ( index = 0; index < length; index++ ) {
                elem = nodes[ index ];
                if ( !elem.style ) {
                    continue;
                }
                show = show === -1 ? !status[index] : show
                elem.style.display = show ?  values[ index ] : "none";
            }
            return nodes;
        }
        $.fn.show =  function() {
            return toggelDisplay( this, 1 );
        }
        $.fn.hide = function() {
            return toggelDisplay( this, 0 );
        }
        //state为true时,强制全部显示,为false,强制全部隐藏
        $.fn.toggle = function( state ) {
            return toggelDisplay( this, typeof state == "boolean" ? state : -1 );
        }
    
        function setOffset(node, options){
            if(node && node.nodeType == 1 ){
                var position = $.css( node, "position" );
                //强逼定位
                if ( position === "static" ) {
                    node.style.position = "relative";
                }
                var curElem = $( node ),
                curOffset = curElem.offset(),
                curCSSTop = $.css( node, "top" ),
                curCSSLeft = $.css( node, "left" ),
                calculatePosition = ( position === "absolute" || position === "fixed" ) &&  [curCSSTop, curCSSLeft].indexOf("auto") > -1,
                props = {}, curPosition = {}, curTop, curLeft;
                if ( calculatePosition ) {
                    curPosition = curElem.position();
                    curTop = curPosition.top;
                    curLeft = curPosition.left;
                } else {
                    //如果是相对定位只要用当前top,left做基数
                    curTop = parseFloat( curCSSTop ) || 0;
                    curLeft = parseFloat( curCSSLeft ) || 0;
                }
    
                if ( options.top != null ) {
                    props.top = ( options.top - curOffset.top ) + curTop;
                }
                if ( options.left != null ) {
                    props.left = ( options.left - curOffset.left ) + curLeft;
                }
                curElem.css( props );
            }
        }
    
        $.fn.offset = function(options){//取得第一个元素位于页面的坐标
            if ( arguments.length ) {
                return (!options || ( !isFinite(options.top) && !isFinite(options.left) ) ) ?  this :
                this.each(function() {
                    setOffset( this, options );
                });
            }
    
            var node = this[0], doc = node && node.ownerDocument, pos = {
                left:0,
                top:0
            };
            if ( !doc ) {
                return pos;
            }
            //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的
            //我们可以通过getBoundingClientRect来获得元素相对于client的rect.
            //http://msdn.microsoft.com/en-us/library/ms536433.aspx
            var box = node.getBoundingClientRect(),win = getWindow(doc),
            root = doc.documentElemen,
            clientTop  = root.clientTop  || 0,
            clientLeft = root.clientLeft || 0,
            scrollTop  = win.pageYOffset ||  root.scrollTop  ,
            scrollLeft = win.pageXOffset ||  root.scrollLeft ;
            // 把滚动距离加到left,top中去。
            // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它
            // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx
            pos.top  = box.top  + scrollTop  - clientTop,
            pos.left = box.left + scrollLeft - clientLeft;
    
            return pos;
        }
    
        $.fn.position = function() {//取得元素相对于其offsetParent的坐标
            var offset, offsetParent , node = this[0],
            parentOffset = {//默认的offsetParent相对于视窗的距离
                top: 0,
                left: 0
            }
            if ( !node ||  node.nodeType !== 1 ) {
                return
            }
            //fixed 元素是相对于window
            if(getter( node, "position" ) === "fixed" ){
                offset  = node.getBoundingClientRect();
            } else {
                offset = this.offset();//得到元素相对于视窗的距离(我们只有它的top与left)
                offsetParent = this.offsetParent();
                if ( offsetParent[ 0 ].tagName !== "HTML"  ) {
                    parentOffset = offsetParent.offset();//得到它的offsetParent相对于视窗的距离
                }
                parentOffset.top  += parseFloat( getter( offsetParent[ 0 ], "borderTopWidth" ) ) || 0;
                parentOffset.left += parseFloat( getter( offsetParent[ 0 ], "borderLeftWidth" ) ) || 0;
            }
            return {
                top:  offset.top  - parentOffset.top - ( parseFloat( getter( node, "marginTop" ) ) || 0 ),
                left: offset.left - parentOffset.left - ( parseFloat( getter( node, "marginLeft" ) ) || 0 )
            };
        }
        //https://github.com/beviz/jquery-caret-position-getter/blob/master/jquery.caretposition.js
        //https://developer.mozilla.org/en-US/docs/DOM/element.offsetParent
        //如果元素被移出DOM树,或display为none,或作为HTML或BODY元素,或其position的精确值为fixed时,返回null
        $.fn.offsetParent = function() {
            return this.map(function() {
                var el = this.offsetParent;
                while ( el && (el.parentNode.nodeType !== 9 ) && getter(el, "position") === "static" ) {
                    el = el.offsetParent;
                }
                return el || document.documentElement;
            });
        }
        $.fn.scrollParent = function() {
            var scrollParent;
            if ((window.VBArray && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
                scrollParent = this.parents().filter(function() {
                    return (/(relative|absolute|fixed)/).test($.css(this,'position')) && (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
                }).eq(0);
            } else {
                scrollParent = this.parents().filter(function() {
                    return (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
                }).eq(0);
            }
            return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
        }
    
        "scrollLeft_pageXOffset,scrollTop_pageYOffset".replace( rmapper, function(_, method, prop ) {
            $.fn[ method ] = function( val ) {
                var node, win, top = method == "scrollTop";
                if ( val === void 0 ) {
                    node = this[ 0 ];
                    if ( !node ) {
                        return null;
                    }
                    win = getWindow( node );//获取第一个元素的scrollTop/scrollLeft
                    return win ? (prop in win) ? win[ prop ] :
                    win.document.documentElement[ method ]  : node[ method ];
                }
                return this.each(function() {//设置匹配元素的scrollTop/scrollLeft
                    win = getWindow( this );
                    if ( win ) {
                        win.scrollTo(
                            !top ? val : $( win ).scrollLeft(),
                            top ? val : $( win ).scrollTop()
                            );
                    } else {
                        this[ method ] = val;
                    }
                });
            };
        });
        var pseudoAdapter = window.VBArray && $.query && $.query.pseudoAdapter
        if(pseudoAdapter){
            pseudoAdapter.hidden = function( el ) {
                return el.type === "hidden" || $.css( el, "display") === "none" ;
            }
        }
    
        function getWindow( node ) {
            return $.type(node,"Window") ?   node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false;
        } ;
    });
    

    css模块依赖于node模块的cssName与cssMap,它们是框架支持CSS3新样式的关键。

    css_fix模块源码(它是用于对旧式IE的支持——IE6-8)

    //=========================================
    //  样式补丁模块
    //==========================================
    define("css_fix", !!top.getComputedStyle, function(){
        $.log("已加载css_fix模块");
        var adapter = $.cssAdapter = {},
        ie8 = !!top.XDomainRequest,
        rfilters = /[\w\:\.]+\([^)]+\)/g,
        salpha = "DXImageTransform.Microsoft.Alpha",
        rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
        rposition = /^(top|right|bottom|left)$/,
        border = {
            thin:   ie8 ? '1px' : '2px',
            medium: ie8 ? '3px' : '4px',
            thick:  ie8 ? '5px' : '6px'
        };
        adapter[ "_default:get" ] = function(node, name){
            //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位
            var ret = node.currentStyle[name];
            if (( rnumnonpx.test(ret) && !rposition.test(ret))) {
                //①,保存原有的style.left, runtimeStyle.left,
                var style = node.style, left = style.left,
                rsLeft =  node.runtimeStyle.left ;
                //②由于③处的style.left = xxx会影响到currentStyle.left,
                //因此把它currentStyle.left放到runtimeStyle.left,
                //runtimeStyle.left拥有最高优先级,不会style.left影响
                node.runtimeStyle.left = node.currentStyle.left;
                //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft
                //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760
                style.left = name === 'fontSize' ? '1em' : (ret || 0);
                ret = style.pixelLeft + "px";
                //④还原 style.left,runtimeStyle.left
                style.left = left;
                node.runtimeStyle.left = rsLeft;
            }
            if( ret == "medium" ){
                name = name.replace("Width","Style");
                //border width 默认值为medium,即使其为0"
                if(arguments.callee(node,name) == "none"){
                    ret = "0px";
                }
            }
            //处理auto值
            if(rposition.test(name) && ret === "auto"){
                ret = "0px";
            }
            return ret === "" ? "auto" : border[ret] ||  ret;
        }
        //========================= 处理 opacity =========================
        adapter[ "opacity:get" ] = function( node ){
            //这是最快的获取IE透明值的方式,不需要动用正则了!
            var alpha = node.filters.alpha || node.filters[salpha],
            op = alpha ? alpha.opacity: 100;
            return ( op /100 )+"";//确保返回的是字符串
        }
        //http://www.freemathhelp.com/matrix-multiplication.html
        adapter[ "opacity:set" ] = function( node, name, value ){
            var currentStyle = node.currentStyle, style = node.style;
            if(!isFinite(value)){//"xxx" * 100 = NaN
                return
            }
            value = (value > 0.999) ? 100: (value < 0.001) ? 0 : value * 100;
            if(!currentStyle.hasLayout)
                style.zoom = 1;//让元素获得hasLayout
            var filter = currentStyle.filter || style.filter || "";
            //http://snook.ca/archives/html_and_css/ie-position-fixed-opacity-filter
            //IE78的透明滤镜当其值为100时会让文本模糊不清
            if(value == 100  ){  //IE78的透明滤镜当其值为100时会让文本模糊不清
                // var str =  "filter: progid:DXImageTransform.Microsoft.Alpha(opacity=100) Chroma(Color='#FFFFFF')"+
                //   "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand',"+
                //   "M11=1.5320888862379554, M12=-1.2855752193730787,  M21=1.2855752193730796, M22=1.5320888862379558)";
                value = style.filter = filter.replace(rfilters, function(a){
                    return /alpha/i.test(a) ? "" : a;//可能存在多个滤镜,只清掉透明部分
                });
                //如果只有一个透明滤镜 就直接去掉
                if(value.trim() == "" && style.removeAttribute){
                    style.removeAttribute( "filter" );
                }
                return;
            }
            //如果已经设置过透明滤镜可以使用以下便捷方式
            var alpha = node.filters.alpha || node.filters[salpha];
    
            if( alpha ){
                alpha.opacity = value ;
            }else{
                style.filter  += (filter ? "," : "")+ "alpha(opacity="+ value +")";
            }
        }
        //========================= 处理 user-select =========================
        //auto——默认值,用户可以选中元素中的内容
        //none——用户不能选择元素中的任何内容
        //text——用户可以选择元素中的文本
        //element——文本可选,但仅限元素的边界内(只有IE和FF支持)
        //all——在编辑器内,如果双击/上下文点击发生在子元素上,改值的最高级祖先元素将被选中。
        //-moz-none——firefox私有,元素和子元素的文本将不可选,但是,子元素可以通过text重设回可选。
        adapter[ "userSelect:set" ] = function( node, name, value ) {
            var allow = /none/.test(value) ? "on" : "",
            e, i = 0, els = node.getElementsByTagName('*');
            node.setAttribute('unselectable', allow);
            while (( e = els[ i++ ] )) {
                switch (e.tagName.toLowerCase()) {
                    case 'iframe' :
                    case 'textarea' :
                    case 'input' :
                    case 'select' :
                        break;
                    default :
                        e.setAttribute('unselectable', allow);
                }
            }
        };
        //========================= 处理 background-position =========================
        adapter[ "backgroundPosition:get" ] = function( node, name, value ) {
            var style = node.currentStyle;
            return style.backgroundPositionX +" "+style.backgroundPositionX
        };
    
    });
    

    github地址


    做个小广告:

    mass Framework是一个模块化的jQuery式框架,拥有jQuery 90%的常用方法,在语言处理,类,特效等方面都做了大量增强,是面向大规模开发的框架。现在jQuery也在做瘦身,把许多不常用的方法废弃掉,这样一来,大家在DOM处理上的API基本一致。mass Framework预计在年底完成升级,完成自己的MVVM框架与一个支持IE6的bootstrap式UI库。

  • 相关阅读:
    centos7.6 使用yum安装mysql5.7
    解决hadoop本地库问题
    docker-compose 启动警告
    docker 安装zabbix5.0 界面乱码问题解决
    docker 部署zabbix问题
    zookeeper 超时问题
    hbase regionserver异常宕机
    (转载)hadoop 滚动升级
    hadoop Requested data length 86483783 is longer than maximum configured RPC length
    zkfc 异常退出问题,报错Received stat error from Zookeeper. code:CONNECTIONLOSS
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2750625.html
Copyright © 2011-2022 走看看