,十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节点数组。