klvoek

klvoek

(十)jQuery.extend代码段

查看jQuery.css实现代码发现css调用了curCss,而这个curCss调用了swap方法,swap方法没有调用其它方法。于是

swap: function(e,o,f) {
    for ( var i in o ) {
        e.style["old"+i] = e.style[i];
        e.style[i] = o[i];
    }
    f.apply( e, [] );
    for ( var i in o )
        e.style[i] = e.style["old"+i];
}

swap方法实现的是将e上的原有属性保存在old前缀的属性上,然后调用指定的函数f,最后还原e上的原有属性。接下来看curCSS的实现

curCSS: function(elem, prop, force) {
    var ret;
    if (!force && elem.style[prop]) {
        ret = elem.style[prop];
    } else if (elem.currentStyle) {
        var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase()});
        ret = elem.currentStyle[prop] || elem.currentStyle[newProp];
    } else if (document.defaultView && document.defaultView.getComputedStyle) {
        prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
        var cur = document.defaultView.getComputedStyle(elem, null);
        if ( cur )
            ret = cur.getPropertyValue(prop);
        else if ( prop == 'display' )
            ret = 'none';
        else             jQuery.swap(elem, { display: 'block' }, function() {
                ret = document.defaultView.getComputedStyle(this,null).getPropertyValue(prop);
            });
    }
     
    return ret;
}

css操作的实现个浏览器之间会有些许区别。主要是ie和非ie浏览器。在ie下使用elem.style[property name] 或者 elem.currentStyle[property name]获取,在非ie浏览器下通过elem.currentStyle[property name]或者getComputedStyle获取。在ie下,ie9以后版本开始了对document.defaultView的支持。curCSS代码结构分为三个if判断结构

当force不为true时并且elem.style[prop]存在,则取elem.style[prop]这个值

当elem.currentStyle存在时,ie下需要将 '-' 后第一个字母大写,非ie则不需要。于是这里对大写和不大写的值都进行了获取。

当document.defaultView存在且document.defaultView.getComputedStyle存在时。将prop字符串 '-' 后第一个字母转换为小写模式。如果getComputedStyle没有返回正确值时,如果获取的是display属性则返回none。否则设置当前元素display = 'block'再获取指定的css值。

接下来看jQuery.css定义:

css: function(e,p) {
    if ( p == "height" || p == "width" ) {
        var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"];
        for ( var i in d ) {
            old["padding" + d[i]] = 0;
            old["border" + d[i] + "Width"] = 0;
        }
        jQuery.swap( e, old, function() {
            if (jQuery.css(e,"display") != "none") {
                oHeight = e.offsetHeight;
                oWidth = e.offsetWidth;
            } else {
                e = $(e.cloneNode(true)).css({
                    visibility: "hidden", position: "absolute", display: "block"
                }).prependTo("body")[0];
                oHeight = e.clientHeight;
                oWidth = e.clientWidth;
                 
                e.parentNode.removeChild(e);
            }
        });
        return p == "height" ? oHeight : oWidth;
    } else if ( p == "opacity" && jQuery.browser.msie )
        return parseFloat( jQuery.curCSS(e,"filter").replace(/[^0-9.]/,"") ) || 1;
    return jQuery.curCSS( e, p );
}

这个css函数获取元素e上p指定的css属性值。这个函数视线里对height、widht和获取ie下的opacity这几个css属性进行了特殊处理。先分析处理代码最少的。对于ie下的opacity属性,ie并没有opacity这个css属性,而是有自己的filter属性。该值的合法值是浮点值。于是代码里使用了正则表达式移除获取的开头的非数字字符和js下的parseFloat函数将处理后的字符串转化成浮点值。对高度和宽度的获取的处理,是受W3C盒子模型的影响。至于W3C盒子模型的特点可以去参考 Google  搜索。由于盒子模型的特点,将padding和border宽度设置为0后我们就可以得到设置的元素的宽度和高度了,而不是盒子模型结合padding和border宽度计算后的宽度和高度。

var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"];for ( var i in d ) {
    old["padding" + d[i]] = 0;
    old["border" + d[i] + "Width"] = 0;
}

接下来的代码则是判断元素的display属性。如果不是none的话,直接取元素的offsetHeight和offsetWidth值。如果是none的话,再不破坏元素e的属性的前提下,需要将元素e克隆一份,对克隆的这一份应用css属性{visibility:"hidden",position:"absolute",display:"block"}并获取clientHeight和clientWidth值。最后清理现场。注意以下代码中使用的css函数是jQuery对象上的css函数,而不是jQuery本身的css函数。

e = $(e.cloneNode(true)).css({
    visibility: "hidden", position: "absolute", display: "block"
}).prependTo("body")[0];

追寻css的定义,在jQuery.fn = jQuery.prototype = { ... } 代码中,css实现非常简单,是对attr函数的封装。

css: function( key, value ) {
        return this.attr( key, value, "curCSS" );
}

分析attr函数的实现

attr: function( key, value, type ) {
    // Check to see if we're setting style values     return key.constructor != String || value != undefined ?
        this.each(function(){
            // See if we're setting a hash of styles             if ( value == undefined )
                // Set all the styles                 for ( var prop in key )
                    jQuery.attr(
                        type ? this.style : this,
                        prop, key[prop]
                    );
             
            // See if we're setting a single key/value style             else                 jQuery.attr(
                    type ? this.style : this,
                    key, value
                );
        }) :
         
        // Look for the case where we're accessing a style value         jQuery[ type || "attr" ]( this[0], key );
}

attr函数的实现里分为两部分设置指定属性值(设置一个属性的写法、设置多个属性的写法),获取指定属性的值。获取属性的值的代码只有一行即

jQuery[ type || "attr" ]( this[0], key );

执行这一句的条件是key是字符串(key.constructor == String)并且value 为undefined,这个时候是需要获取指定属性的值。需要注意的是获取css属性值和获取非css属性值是如何实现的。jQuery[type||'attr']起到了区别获取css属性或者非css属性的效果。控制是否获取css属性的参数type有外界传入,上面css函数对attr函数的引用就是一个例子。对于设置指定属性的值,我们可以看到代码里区分为传入了value和没传入value两部分。type依旧作为区分是设置css属性或者非css属性的参数。

这样init函数就算是解析完成了,通过分析可以看出init函数完成对jQuery对象上的功能的拓展。在init同代码段的extend中还有一个函数没有分析,他就是clean函数。

clean: function(a) {
    var r = [];
    for ( var i = 0; i < a.length; i++ ) {
        if ( a[i].constructor == String ) {
            var table = "";
            if ( !a[i].indexOf("<thead") || !a[i].indexOf("<tbody") ) {
                table = "thead";
                a[i] = "<table>" + a[i] + "</table>";
            } else if ( !a[i].indexOf("<tr") ) {
                table = "tr";
                a[i] = "<table>" + a[i] + "</table>";
            } else if ( !a[i].indexOf("<td") || !a[i].indexOf("<th") ) {
                table = "td";
                a[i] = "<table><tbody><tr>" + a[i] + "</tr></tbody></table>";
            }
            var div = document.createElement("div");
            div.innerHTML = a[i];
            if ( table ) {
                div = div.firstChild;
                if ( table != "thead" ) div = div.firstChild;
                if ( table == "td" ) div = div.firstChild;
            }
            for ( var j = 0; j < div.childNodes.length; j++ )
                r.push( div.childNodes[j] );
        }else if ( a[i].jquery || a[i].length && !a[i].nodeType ){
         
            for ( var k = 0; k < a[i].length; k++ )
                r.push( a[i][k] );
        }else if ( a[i] !== null ){
         
            r.push( a[i].nodeType ? a[i] : document.createTextNode(a[i].toString()) );
        }
    }
    return r;
}

初次看到clean中的代码对table段的处理不明所以,但是结合我们之前提到jQuery可以把html文本处理成jQuery对象的提示就不难理解。clean中有三个if判断。

第一个if判断,对传入的是文本的处理。这段代码里有对不完整的table文本进行处理。比如传入字符串'<thead><tr><td></td></tr></thead>' 等。需要组成完整的table描述字符串,否则不能正确解析。接下来就是通过div.innerHTML = a[i]; 将html文本变成DOM元素。在接下来是根据之前处理过程中产生的table变量对产生的DOM元素进行处理。

if ( table ) {
    div = div.firstChild;
    if ( table != "thead" ) div = div.firstChild;
    if ( table == "td" ) div = div.firstChild;
}

这段代码的逻辑比较精细,需要仔细理解啊。主要是去除完整table中对应传入的html所描述的节点。最后就是将产生的DOM节点加入结果集中。

第二个if判断,如果是jQuery对象或者当前a[i]有length属性并且a[i]不是DOM节点时。将a[i][]数组中的值一个个加入结果集中。

第三个if判断,如果a[i]不为null。如果a[i]是DOM节点则直接加入结果集。如果a[i]不是DOM节点,则把a[i]的字符串表示创建陈一个document.crateTextNode加入结果集

最后返回产生的结果集。需要注意,返回额这个结果集是一个DOM节点数组。

标签: jquery, css
Posted by klvoek @ 2012-2-12 16:08:43 阅读(76) 评论(0)
上一篇:(九)jQuery.extend代码段
下一篇:(十一)jQuery.extend代码段

我也来参与讨论

你还可以输入600/600个字符 发表评论
称呼: (必填) 登录 | 开通博客
邮箱: (选填) 你的邮箱地址不会被公开
网站: (选填)
验证码: (必填)
看不清换一张 看不清楚换一张