您的位置:首页 > Web前端 > JQuery

jQuery源码学习5——工具方法之attr parents sibling clean

2015-11-16 10:52 633 查看
(1)、attr

attr: function(elem, name, value){
var fix = {
"for": "htmlFor",
"class": "className",
"float": "cssFloat",
innerHTML: "innerHTML",
className: "className"
};

if ( fix[name] ) {
if ( value != undefined ) elem[fix[name]] = value;
return elem[fix[name]];
} else if ( elem.getAttribute ) {
if ( value != undefined ) elem.setAttribute( name, value );
return elem.getAttribute( name, 2 );
} else {
name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
if ( value != undefined ) elem[name] = value;
return elem[name];
}
},


从形参上基本上可以猜测出这个方法的作用

$.attr(oDiv,"abc")是获取到oDiv的abc属性的值

$.attr(oDiv,"abc",1)是将oDiv的abc属性值设置为1

最开始在fix这个对象中枚举了需要修正的一些属性

接下来先在if中判断传进来的属性是否在fix所枚举的属性中

如果在的话要使用修正后的属性,例如

如果调用的时候是这样调:$.attr(oDiv,"class")

那么在该方法内部就会被修正为oDiv["className"]

然后再分析接下来的else if

else if的条件是判断elem有么有getAttribute这个方法

如果有这个方法那就通过get/setAttribute来获取/设置属性即可

如果元素不支持getAttribute这个方法就会走到最后的else中

然后将形如aaa-bbb-ccc的属性转换成驼峰式的名字再进行获取/设置

经过测试,如果通过下面这样给元素设置了自定义属性:

<div id="div1" abc="123"></div>


那么在IE8- 的浏览器中通过oDiv.abc或者oDiv.getAttribute("abc");都可以得到123

而在IE9+ chrome FF中通过oDiv.abc得到了undefined,而oDiv.getAttribute("abc")可以得到123

测试完了之后感觉else这个分支不知道什么时候进入了

(2)、parents

parents: function( elem ){
var matched = [];
var cur = elem.parentNode;
while ( cur && cur != document ) {
matched.push( cur );
cur = cur.parentNode;
}
return matched;
},


从名字上能看出来这个方法是要获得elem的父元素

而且parents还是复数形式

所以会一直向顶层追踪

在方法内部用递归做到了这一点

最后把所有取到的父元素放到match中直接返回

match数组中第一个元素是elem的直接父级,最后一个元素是最顶层的父级

(3)、sibling

sibling: function(elem, pos, not) {
var elems = [];

var siblings = elem.parentNode.childNodes;
for ( var i = 0; i < siblings.length; i++ ) {
if ( not === true && siblings[i] == elem ) continue;

if ( siblings[i].nodeType == 1 )
elems.push( siblings[i] );
if ( siblings[i] == elem )
elems.n = elems.length - 1;
}

return jQuery.extend( elems, {
last: elems.n == elems.length - 1,
cur: pos == "even" && elems.n % 2 == 0 || pos == "odd" && elems.n % 2 || elems[pos] == elem,
prev: elems[elems.n - 1],
next: elems[elems.n + 1]
});
},


sibling的意思是兄弟姐妹,所以这个方法是用来获取传入的elem的兄弟元素的

至于第二个参数pos是位置的意思,通过搜索jQuery内部使用sibling的情况发现

pos的值可以是数字、"odd",在sibling这个函数的函数体中还发现了pos的值还可以是"even"

至于第三个参数not,还没有发现它在jQuery源码的哪个地方用到了

在该方法内部,先循环遍历了elem的直接父级下的所有元素

值得注意是这里面有文本节点、注释节点、元素节点等等

所以里面的if条件作了一定的筛选

先来看下第一个if条件,如果not是true而且遍历到的元素(sibling[i])就是传入的elem时结束本次循环

到这里我们就知道这个sibling方法的第三个参数not到底是什么意思了

如果not传进来true,就代表将来返回的结果集中不包含elem本身,反之则包含

第二个if条件,则是将元素节点筛选出来放入elems

第三个if条件,给elems加了一个属性n,这个属性n代表elem在数组elems中的位置

接下来再在elems上扩展last cur prev next四个属性

最后将elems返回回去

last类型是bool,表示elem是否是elems里面的最后一个

prev next分别返回elem的前一个元素和后一个元素

如果elems.n是0,即传进来的elem是所有兄弟元素中的第一个时,那么elems[elems.length-1]就得到elems[-1]

也就是数组下标越界了

但是经过测试并没有报错,只是返回undefined

最后再看最麻烦的cur

说实话,日常开发的话最好加上小括号来表明优先级,这样的代码看起来不太友好

所以我画了一张图,这样看起来更明显



cur有分成了三种情况,最简单的就是最后一个红框

这是传入的pos是数字的情况,返回值的意思就是在所有的兄弟元素中,elem是不是第pos个元素

第二种、第三种情况就是pos分别传入even和odd的情况

pos传入even时,返回值的意思就是在所有的兄弟元素中,elem的索引是不是偶数

pos传入odd自然不用多说

(4)、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;
},


在这个系列的第二篇文章中提到过,有3个地方用到了clean 最开始的jQuery构造函数里面 domManip里面 wrap里面

但是clean这个函数具体用在什么场合下,还是不太清楚

不过没有关系,我们先大概了解一下它是干什么的,等分析到domManip wrap等这些函数里面再去看

通过查看clean函数被调用的三个地方来看,它貌似是接收一个形如 ["<div></div>",$("a"),"<table></table>"]的数组参数a

数组里面的每一项都会被遍历到,遍历的过程中会根据其类型走三个不同的分支

第一个分支是处理字符串的,第二个分支处理jQuery对象,第三个分支处理非空对象

先看较为简单的第二个分支,进入这个分支有两个渠道

第一个渠道是a[i]有length属性并且没有nodeType属性

不能有nodeType属性说明a[i]不是DOM元素,有length属性又说明得是类数组元素

第二个渠道是a[i]有jquery属性,即a[i]是jQuery对象

不论哪种渠道,最后r中存放的都是这些类数组集合中的各项

最后一个else if分支看起来也是同样的道理

最后再来看很复杂的第一个if分支

处理["<div></div>","<table></table>"]的情况

里面有对是不是thead tbody tr td标签做了判断

如果不是这一系列的标签,那代码就简化成了这样:

var div = document.createElement("div");
div.innerHTML = a[i];
for ( var j = 0; j < div.childNodes.length; j++ ){
r.push( div.childNodes[j] );
}


其实就是把string类型的标签转换为真正的DOM元素,最后存放在r中

对于表格家族的标签,处理的过程好像是当传入thead tbody tr的时候

就在这些标签的外层包裹table标签

当传入td的时候,就在td外层包裹table tbody tr

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


这段代码的作用是找到距离传入的元素最近的父元素

通过以上分析和我的猜测,再联想到这个方法的名字,感觉clean是要返回一个纯粹的数组

这个数组里面存放的就是原生DOM对象

为什么说是一个纯粹的数组呢?因为类数组对象有可能形如:

{
"1":oDiv1,
"2":oDiv2,
"3":oDiv3,
"length":3,
"aaa":123,
"bbb":456
}


也就是说类数组对象上除了有length属性之外,还有aaa bbb这些东西

而这个方法的功能之一就是把这些aaa bbb之类的属性给clean掉

功能之二就是将"<div></div>"转换成对应的DOM元素
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: