欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

jQuery 源码分析(十二) 数据操作模块 html特性 详解

程序员文章站 2022-05-18 19:31:16
jQuery的属性操作模块总共有4个部分,本篇说一下第1个部分:HTML特性部分,html特性部分是对原生方法getAttribute()和setAttribute()的封装,用于修改DOM元素的特性的 jQuery的静态方法含有如下API: $.attr(elem, name, value) ;设 ......

jquery的属性操作模块总共有4个部分,本篇说一下第1个部分:html特性部分,html特性部分是对原生方法getattribute()和setattribute()的封装,用于修改dom元素的特性的

jquery的静态方法含有如下api:

  • $.attr(elem, name, value)  ;设置或读取html属性,该方法有三种用法:

                    ·$.attr(elem,name,null)       ;如果value为null则调用jquery.removeattr(elem, name)删除该属性
                    ·$.attr(elem,name,value)    ;设置elem元素的name属性值为value。
                    ·$.attr(elem,name)              ;获取elem元素的name属性

  • $.removeattr(elem, name)  ;从dom元素elem上移除name属性,name可以是单个字符串,也可以是空格分隔的多个html属性。对于应布尔属性会同步设置对应的dom属性为false。

jquery/$ 实例方法(可以通过jquery实例调用的):

  • attr(name, value)           ;移除、设置html属性,有以下方法

      ·attr(obj)             ;参数1是对象时                             ;access()函数中验证 表示一次性设置多个属性
      ·attr(name,value)       ;为每个匹配元素设置一个html属性                  ;value可以是一个函数,取值为返回值,也可以为null时表示删除该属性    
      ·attr(name,null)         ;参数2为null时表示删除所有匹配元素的name特性,间接调用removeattr()
      ·attr(name)              ;参数1是字符串时,参数2未指定或者设置为false    ;表示获取第一个匹配元素的html属性值。

  • removeattr(name)                    ;移除每一个匹配元素的一个或多个html属性,name是要是移除的html属性,多个可以用空格分隔

举个栗子:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <a>链接</a>
    <button>淘宝</button>                <!--点击后a标签将导航到淘宝-->
    <button>百度</button>                <!--点击后a标签将导航到百度-->
    <button>移除</button>                <!--点击后a标签将取消导航-->
    <script>
        let a      = document.getelementsbytagname('a')[0],                //获取a标签的引用
            b1    = document.getelementsbytagname('button')[0],            //淘宝按钮的引用
            b2    = document.getelementsbytagname('button')[1],            //百度按钮的引用
            b3    = document.getelementsbytagname('button')[2];            //移除按钮的引用
        b1.addeventlistener('click',function(){
            $.attr(a,'href','http://www.taobao.com');            //通过jquery的静态方法设置href属性
        })
        b2.addeventlistener('click',function(){
            $('a').attr('href','http://www.baidu.com')            //通过jquery的实例方法设置href属性
        })
        b3.addeventlistener('click',function(){
            $("a").removeattr('href')                            //移除href属性
        })
    </script>    
</body>
</html>

渲染的页面如下:

jQuery 源码分析(十二) 数据操作模块 html特性 详解

 此时对应的dom结构如下:

jQuery 源码分析(十二) 数据操作模块 html特性 详解

  当我们点击淘宝按钮后页面变为了如下:

jQuery 源码分析(十二) 数据操作模块 html特性 详解

dom修改了这样子:

jQuery 源码分析(十二) 数据操作模块 html特性 详解

此时点击这个a标签将链接到淘宝网,然后我们点击百度,链接会链接到百度去的,最后点击移除时,该a标签又会变为初始化的状态。这就是jquery的html特性操作

 

源码分析


 $.attr和$.removeattr实现如下:

jquery.extend({
    attr: function( elem, name, value, pass ) {            //设置或读取html属性,是对原生方法getattribute()和setattribute()的简化
        var ret, hooks, notxml,
            ntype = elem.nodetype;

        // don't get/set attributes on text, comment and attribute nodes
        if ( !elem || ntype === 3 || ntype === 8 || ntype === 2 ) {        //如果elem为空 或者是文本、注释、属性节点
            return;                                                            //直接返回,不接着处理
        }

        if ( pass && name in jquery.attrfn ) {
            return jquery( elem )[ name ]( value );
        }

        // fallback to prop when attributes are not supported
        if ( typeof elem.getattribute === "undefined" ) {                //如果不支持方法getattribute
            return jquery.prop( elem, name, value );                        //则调用对应的dom属性
        }

        notxml = ntype !== 1 || !jquery.isxmldoc( elem );                //判断elem是否不是xml文档元素

        // all attributes are lowercase
        // grab necessary hook if one is defined
        if ( notxml ) {
            name = name.tolowercase();
            hooks = jquery.attrhooks[ name ] || ( rboolean.test( name ) ? boolhook : nodehook );
        }

        if ( value !== undefined ) {                                    //如果传入了参数value,表示是设置值

            if ( value === null ) {                                            //若值是null,则移除该name属性
                jquery.removeattr( elem, name );
                return;

            } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {    //优先调用对应的修正对象的修正方法set()
                return ret;

            } else {
                elem.setattribute( name, "" + value );                    //否则调用原生方法setattribute()设置html属性
                return value;
            }

        } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {    //如果未传入参数value,优先调用对应的修正对象的修正方法get()
            return ret;

        } else {

            ret = elem.getattribute( name );                            //否则调用原生方法getattrubute()读取html属性。

            // non-existent attributes return null, we normalize to undefined
            return ret === null ?
                undefined :
                ret;
        }
    },

    removeattr: function( elem, value ) {                    //从dom元素上移除一个或多个html属性,多个html属性用空格分隔。是对removeattribute的封装和扩展。
        var propname, attrnames, name, l,
            i = 0;

        if ( value && elem.nodetype === 1 ) {                    //如果设置了value参数 且 elem是一个元素节点
            attrnames = value.tolowercase().split( rspace );        //执行后attrnames是一个数组,保存了要移除的属性名:比如:array [ "id", "name" ] rspace = /\s+/,    
            l = attrnames.length;                                    //需要移除的属性的个数

            for ( ; i < l; i++ ) {                                    //遍历数组attrnames,逐个移除html属性。
                name = attrnames[ i ];                                    //name是要移除的属性名

                if ( name ) {
                    propname = jquery.propfix[ name ] || name;                    //如果属性名name需要修正,则修正属性

                    // see #9699 for explanation of this approach (setting first, then removal)
                    jquery.attr( elem, name, "" );                                //先将html属性设置为空字符串,以解决webkit内核浏览器不能
                    elem.removeattribute( getsetattribute ? name : propname );    //调用原生方法removeattribute删除对应的属性,如果jquery.support.getsetattribute为true则删除name属性,如果为false,表示在ie6、7下则删除特殊属性。

                    // set corresponding property to false for boolean attributes
                    if ( rboolean.test( name ) && propname in elem ) {            //对应布尔属性,同步设置对应的dom属性为false
                        elem[ propname ] = false;
                    }
                }
            }
        }
    },
    /**/
})

对于jquery实例来说,它调用了不同的工具函数,最后还是执行上面讲解的静态方法的,如下:

jquery.fn.extend({
    attr: function( name, value ) {            //移除、设置html属性
        return jquery.access( this, name, value, true, jquery.attr );    //调用了jquery.access工具函数,参数5传入了jquery.attr
    },

    removeattr: function( name ) {            //移除html属性
        return this.each(function() {            //通过each函数方法,依次执行jquery.removeattr()
            jquery.removeattr( this, name );
        });
    },
    /*略*/
})

由于jquery中的特性、dom属性和样式操作的函数参数可以是差不多的,jquery就定义了一个access函数,为.attr()、.prop()、.css()提供支持,这样我们通过jquery实例设置特性、属性和样式时可以传入的参数类型,例如:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <a>链接</a>
    <script>
        $('a').attr('href','http://www.cnblogs.com')            //参数2是个字符串
        $('a').attr('href',()=>'http://www.cnblogs.com')        //参数2还是是个函数
        $('a').attr({href:'http://www.cnblogs.com'})            //也可以传入一个对象        
    </script>    
</body>
</html>

我们传入不同的参数,都可以实现设置特性的效果,这就是$.access的作用,$.access的源码实现如下:

jquery.extend({                
    access: function( elems, key, value, exec, fn, pass ) {        //为集合中的元素设置一个或多个属性值,或者读取第一个元素的属性值
        var length = elems.length;

        // setting many attributes
        if ( typeof key === "object" ) {                            //如果key是对象,表示要设置多个属性,则遍历该对象循环执行.access函数
            for ( var k in key ) {
                jquery.access( elems, k, key[k], exec, fn, value );
            }
            return elems;
        }

        // setting one attributes
        if ( value !== undefined ) {                                //如果参数value不是undefined,表示要设置单个属性
            // optionally, function values get executed if exec is true
            exec = !pass && exec && jquery.isfunction(value);            //修正exec参数。如果没有传入pass参数或者该参数值是false,且参数exec为true,且value是函数则设置exec为true,否则exec为false。

            for ( var i = 0; i < length; i++ ) {
                fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );        //遍历元素集合elems,为每个元素调用回调函数fn
            }

            return elems;                                                //遍历完成后返回元素elems。以支持链式操作
        }

        // getting an attribute
        return length ? fn( elems[0], key ) : undefined;            //当value参数为空,且元素集合elems不为空则获取第一个匹配元素相关的信息,即执行fn函数
    },
    /**/
})

 writer by:大沙漠 qq:22969969

之后的dom属性和样式操作都会借用access这个工具方法的。