zoukankan      html  css  js  c++  java
  • 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元素

  • 相关阅读:
    【IdentityServer4文档】- 整体情况
    【IdentityServer4文档】- 欢迎来到 IdentityServer4 (ASP.NET Core 3.x)
    证券相关基础概念
    [基于NetCore的简单博客系统]-登录
    JAVA mysql数据库 配置
    简单验证码识别及登陆并下载页面
    Java 无法初始化Connection的问题
    MUI scroll 定位问题
    CentOS7 安装 MySql
    ASP.NET CORE 2.0 文档中文正式版已经出来了
  • 原文地址:https://www.cnblogs.com/zhaohuiziwo901/p/4962231.html
Copyright © 2011-2022 走看看