jQuery 源码分析(十二) 数据操作模块 html特性 详解_白澜_前端开发者

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

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

  • $.attr(elem, name, value)  ;设置或读取html属性,该方法有三种用法:
  • $.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。
  • $.removeAttr(elem, name)  ;从DOM元素elem上移除name属性,name可以是单个字符串,也可以是空格分隔的多个html属性。对于应布尔属性会同步设置对应的DOM属性为false。
  • jquery/$ 实例方法(可以通过jquery实例调用的):

    • attr(name, value)           ;移除、设置html属性,有以下方法
  • 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属性,多个可以用空格分隔
  • 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>
    <!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>

    <!DOCTYPE html><html lang=”en”><head><meta charset=”UTF-8″><title></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>

    渲染的页面如下:

     此时对应的DOM结构如下:

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

    DOM修改了这样子:

    此时点击这个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.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.extend({
    attr:
    function//设置或读取html属性,是对原生方法getAttribute()和setAttribute()的简化var ret, hooks, notxml,
    nType
    elem.nodeType;

    // don’t get/set attributes on text, comment and attribute nodesif//如果elem为空 或者是文本、注释、属性节点return//直接返回,不接着处理 }

    ifin jQuery.attrFn ) {
    return jQuery( elem )[ name ]( value );
    }

    // Fallback to prop when attributes are not supportediftypeof//如果不支持方法getAttributereturn//则调用对应的DOM属性 }

    notxml //判断elem是否不是xml文档元素// All attributes are lowercase// Grab necessary hook if one is definedif ( notxml ) {
    name
    name.toLowerCase();
    hooks
    boolHook : nodeHook );
    }

    if//如果传入了参数value,表示是设置值ifnull//值是null,则移除该name属性 jQuery.removeAttr( elem, name );
    return;

    } elseifin//优先调用对应的修正对象的修正方法set()return ret;

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

    } elseifinnull//如果未传入参数value,优先调用对应的修正对象的修正方法get()return ret;

    } else {

    ret //否则调用原生方法getAttrubute()读取html属性。// Non-existent attributes return null, we normalize to undefinedreturnnull
    undefined :
    ret;
    }
    },

    removeAttr: function//从DOM元素上移除一个或多个html属性,多个html属性用空格分隔。是对removeAttribute的封装和扩展。var propName, attrNames, name, l,
    i
    ;

    if//如果设置了value参数 且 elem是一个元素节点//执行后attrNames是一个数组,保存了要移除的属性名:比如:Array [ “id”, “name” ] rspace = /\s+/, //需要移除的属性的个数for//遍历数组attrNames,逐个移除html属性。//name是要移除的属性名if ( name ) {
    propName
    //如果属性名name需要修正,则修正属性// See #9699 for explanation of this approach (setting first, then removal)//先将html属性设置为空字符串,以解决Webkit内核浏览器不能//调用原生方法removeAttribute删除对应的属性,如果jQuery.support.getSetAttribute为true则删除name属性,如果为false,表示在IE6、7下则删除特殊属性。调用原生方法removeAttribute删除对应的属性// Set corresponding property to false for boolean attributesifin//对应布尔属性,同步设置对应的DOM属性为falsefalse;
    }
    }
    }
    }
    },
    /**/
    })

    对于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.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.fn.extend({
    attr:
    function//移除、设置html属性returnthistrue//调用了jQuery.access工具函数,参数5传入了jQuery.attr },

    removeAttr: function//移除html属性returnthisfunction//通过each函数方法,依次执行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>
    <!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>

    <!DOCTYPE html><html lang=”en”><head><meta charset=”UTF-8″><title></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的源码实现如下:

    我们传入不同的参数,都可以实现设置特性的效果,这就是$.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函数
        },
        /**/
    })
    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函数
        },
        /**/
    })

    jQuery.extend({
    access:
    function//为集合中的元素设置一个或多个属性值,或者读取第一个元素的属性值var elems.length;

    // Setting many attributesiftypeof//如果key是对象,表示要设置多个属性,则遍历该对象循环执行.access函数forvarin key ) {
    jQuery.access( elems, k, key[k], exec, fn, value );
    }
    return elems;
    }

    // Setting one attributesif//如果参数value不是undefined,表示要设置单个属性// Optionally, function values get executed if exec is true//修正exec参数。如果没有传入pass参数或者该参数值是false,且参数exec为true,且value是函数则设置exec为true,否则exec为false。forvar ) {
    fn( elems[i], key, exec
    //遍历元素集合elems,为每个元素调用回调函数fn }

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

    // Getting an attributereturn//当value参数为空,且元素集合elems不为空则获取第一个匹配元素相关的信息,即执行fn函数 },
    /**/
    })

     writer by:大沙漠 QQ:22969969

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

    » 本文来自:前端开发者 » 《jQuery 源码分析(十二) 数据操作模块 html特性 详解_白澜_前端开发者》
    » 本文链接地址:https://www.rokub.com/73720.html
    » 您也可以订阅本站:https://www.rokub.com
    赞(0)
    64K

    评论 抢沙发

    评论前必须登录!