• 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库。

  • 相关阅读:
    阿里云ecs服务器wamp内网可以访问,外网ip、域名无法访问
    python- 粘包 struct,socketserver
    python-网络编程
    python-模块-包
    python- 异常
    python-模块 time, os, sys
    python_模块 collections,random
    python_模块 hashlib ,configparser, logging
    python_ 模块 json pickle shelve
    python-面向对象中的特殊方法 ,反射,与单例模式
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2750625.html
Copyright © 2020-2023  润新知