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

(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元素

posted on 2015-11-16 10:52  特拉法尔加  阅读(265)  评论(0编辑  收藏  举报