1. 程式人生 > >jQuery07源碼 (3803 , 4299) attr() prop() val() addClass()等 : 對元素屬性的操作

jQuery07源碼 (3803 , 4299) attr() prop() val() addClass()等 : 對元素屬性的操作

tex default des selected XML can 正則 jquer bin

var nodeHook, boolHook,
    rclass = /[\t\r\n\f]/g,
    rreturn = /\r/g,
    rfocusable = /^(?:input|select|textarea|button)$/i;

jQuery.fn.extend({
    attr: function( name, value ) {
//遍歷this
//arguments.length > 1,jQuery.attr(this[i],name,value),返回this
//arguments.length <= 1,jQuery.attr(this[i],name,value),返回this
return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each(function() { jQuery.removeAttr( this, name ); }); }, prop: function( name, value ) { return jQuery.access( this
, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { return this.each(function() { delete this[ jQuery.propFix[ name ] || name ]; }); }, //$(‘.div1‘).addClass(‘box2 box3‘); addClass: function( value ) { var classes, elem, cur, clazz, j, i
= 0, len = this.length, proceed = typeof value === "string" && value;//是字符串返回字符串,不是返回flase /* $(‘div‘).addClass( function(index){ alert(index); return ‘box‘+index; }); */ if ( jQuery.isFunction( value ) ) { console.log(this);//這裏的this是jQuery對象,Object { 0: <div#div1.box>, 1: <div#div2.box>, 2: <div#div3.box>, length: 3},通過return ( context || rootjQuery ).find(‘.div1‘)原生方法獲得,裏面每一個是節點對象不是jQuery對象 return this.each(function( j ) { console.log(this);//這裏不是jQuery對象是dom節點對象,<div id=‘div1‘></div>,<div id=‘div2‘></div>,<div id=‘div3‘></div> console.log(jQuery( this ));//jQuery( this )是jQuery對象,Object { 0: <div#div1.box>, context: <div#div1.box>, length: 1 },Object { 0: <div#div2.box>, context: <div#div2.box>, length: 1 },Object { 0: <div#div3.box>, context: <div#div3.box>, length: 1 } /*jQuery( this )走的是 if ( selector.nodeType ) {//節點都有nodeType屬性 this.context = this[0] = selector; this.length = 1; return this;*/ jQuery( this ).addClass( value.call( this, j, this.className ) ); }); } if ( proceed ) { // 把字符串正則分割成數組 classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ]; //不是元素節點返回false,elem.className元素有沒有class屬性,有就合並(重復不合並),cur是之前的class cur = elem.nodeType === 1 && ( elem.className ? //非空格轉換成空格 ( " " + elem.className + " " ).replace( rclass, " " ) : " " ); if ( cur ) { j = 0; while ( (clazz = classes[j++]) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } }//前後去空格 elem.className = jQuery.trim( cur ); } } } return this; }, removeClass: function( value ) { var classes, elem, cur, clazz, j, i = 0, len = this.length, //先執行&&再||,proceed為true參數長度是0刪除所有或者參數是字符串,為false傳的不是字符串 proceed = arguments.length === 0 || typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).removeClass( value.call( this, j, this.className ) ); }); } if ( proceed ) { classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ]; // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : "" ); if ( cur ) { j = 0; while ( (clazz = classes[j++]) ) { // Remove *all* instances while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { cur = cur.replace( " " + clazz + " ", " " ); } } elem.className = value ? jQuery.trim( cur ) : ""; } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value; //$(‘#div1‘).toggleClass(‘box2 box3‘,true);//有沒有都是add //$(‘#div1‘).toggleClass(‘box2 box3‘,false);//有沒有都是刪除 if ( typeof stateVal === "boolean" && type === "string" ) {//真就添加,假就刪除 return stateVal ? this.addClass( value ) : this.removeClass( value ); } if ( jQuery.isFunction( value ) ) { return this.each(function( i ) { jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); }); } //$(‘#div1‘).toggleClass(‘box2 box3‘) return this.each(function() { if ( type === "string" ) { // toggle individual class names var className, i = 0, self = jQuery( this ),//轉成jQuery對象,hasClass是jQuery對象的方法。 //空格分割成數組 classNames = value.match( core_rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) { // check each className given, space separated list if ( self.hasClass( className ) ) { self.removeClass( className ); } else { self.addClass( className ); } } // Toggle whole class name //$(‘#div1‘).toggleClass(false); } else if ( type === core_strundefined || type === "boolean" ) { if ( this.className ) { // store className if set data_priv.set( this, "__className__", this.className ); } // If the element has a class name or if we‘re passed "false", // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; } }); }, hasClass: function( selector ) { var className = " " + selector + " ", i = 0, l = this.length; for ( ; i < l; i++ ) { if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { return true; } } return false; }, val: function( value ) { var hooks, ret, isFunction, elem = this[0]; //$(‘#input1‘).val() if ( !arguments.length ) {//獲取 if ( elem ) {//只獲取第一個元素 //hooks兼容處理,jQuery.valHooks[ elem.type ]在valHooks 這個json中找不到就找jQuery.valHooks[ elem.nodeName.toLowerCase() ] hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; /* valHooks: { hooks = option: { //elem.type || elem.nodeName.toLowerCase() get: function( elem ) {} }, hooks = select: { get: function( elem ) {}, set: function( elem, value ) {} } 下面的: hooks = radio: { set: function( elem ) {} get: function( elem, value ) {} }, hooks = checkbox: { set: function( elem ) {}, get: function( elem, value ) {} } } */ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; } //不再hooks裏面 ret = elem.value; return typeof ret === "string" ? // handle most common string cases ret.replace(rreturn, "") : // handle cases where value is null/undef or number ret == null ? "" : ret; } return; } //設置 isFunction = jQuery.isFunction( value ); return this.each(function( i ) { var val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, jQuery( this ).val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) {//$(‘#input1‘).val(null); val = ""; } else if ( typeof val === "number" ) {//$(‘#input1‘).val(123123); val += "";//轉成字符串 } else if ( jQuery.isArray( val ) ) {//$(‘#input2‘).val([‘hello‘]); val = jQuery.map(val, function ( value ) { return value == null ? "" : value + ""; }); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } }); } }); jQuery.extend({//靜態屬性只能通過jQuery靜態方式調 valHooks: {//option-get,select-get.select-set 兼容性處理 option: { get: function( elem ) { // attributes.value is undefined in Blackberry 4.7 but // uses .value. See #6932 var val = elem.attributes.value; //val不存在輸出elem.value,val存在specified為false走elem.text return !val || val.specified ? elem.value : elem.text; } }, select: { //$(‘select‘).val() get: function( elem ) { var value, option, options = elem.options,//下拉選項 index = elem.selectedIndex,//當前索引值 //select只選了一個或者沒有選,one為true,就是單選 one = elem.type === "select-one" || index < 0, //one為true時單選values是空,one是false時多選values是一個數組存儲所有的選擇的多個 values = one ? null : [], //單選時max是當前索引加1,多選時是下拉選項的長度 max = one ? index + 1 : options.length, // i = index < 0 ? max /*index < 0沒有選擇時one=true,i=max=0*/ :one ? index/*index >= 0有選擇時,select-one單選one=true,i=index,max=index+1,*/ :0 /*index >= 0有選擇時,不是select-one多選one=false,i=0,max=options.length*/ ; // 沒有選擇不進入循環,不獲取select的val() //有選擇單選,i=index,只獲取index的val() //有選擇好多選,全部獲取 for ( ; i < max; i++ ) { option = options[ i ];//js對象 // IE6-9 doesn‘t update selected after form reset (#2551) if ( ( option.selected || i === index ) && // Don‘t return options that are disabled or in a disabled optgroup ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val();//轉成jQuery對象 // We don‘t need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } return values; }, // $(‘#select‘).val(111);//111被選中了 set: function( elem, value ) { var optionSet, option, options = elem.options,//所有的下拉選項,js對象 values = jQuery.makeArray( value ),//轉成數組 i = options.length; while ( i-- ) {//遍歷 option = options[ i ]; if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {//在數組裏面就把她設為選中 optionSet = true; } } // force browsers to behave consistently when non-matching value is set if ( !optionSet ) {//都沒有 elem.selectedIndex = -1; } return values; } } }, attr: function( elem, name, value ) { var hooks, ret, nType = elem.nodeType; // 節點不存在,或者文本、屬性、註釋節點 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } // core_strundefined = typeof undefined, if ( typeof elem.getAttribute === core_strundefined ) { //$(document).attr(‘title‘,‘hello‘); 走這裏通過.設置 return jQuery.prop( elem, name, value ); } // 1是元素節點, if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { name = name.toLowerCase(); //只有type才做兼容性處理 hooks = jQuery.attrHooks[ name ] || //$(‘input‘).attr(‘checked‘,true);//沒問題,做兼容了 ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); } if ( value !== undefined ) {//設置 //$(‘#div1‘).attr(‘miaov‘,null); 調用remove if ( value === null ) { jQuery.removeAttr( elem, name ); //hooks中,set方法存在,就調用set方法並且返回值存在,就返回返回值 } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret;//有兼容性執行兼容操作,返回值 } else {//沒有兼容性操作設置值 elem.setAttribute( name, value + "" ); return value; } //hooks中,get方法存在,就調用get方法並且返回值存在,就返回返回值 } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {//獲取,有兼容性返回值 return ret; } else {//獲取沒有兼容性時 ret = jQuery.find.attr( elem, name ); // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; } }, removeAttr: function( elem, value ) { var name, propName, i = 0, //$(‘#div1‘).removeAttr(‘maio href id‘); attrNames = value && value.match( core_rnotwhite );//core_rnotwhite = /\S+/g, 非空格,返回數組 if ( attrNames && elem.nodeType === 1 ) { while ( (name = attrNames[i++]) ) { /* propFix: { "for": "htmlFor", "class": "className" }, */// $(‘#div1‘).removeAttr(‘class‘); propName = jQuery.propFix[ name ] || name; // Boolean attributes get special treatment (#10870) if ( jQuery.expr.match.bool.test( name ) ) { // $(‘#div1‘).removeAttr(‘checked‘); elem[ propName ] = false; } elem.removeAttribute( name );//調用原生 } } }, //hooks = jQuery.attrHooks[ name ] attrHooks: { type: {//只有name = ‘type‘,才會有有兼容性判斷。 set: function( elem, value ) {//只有set說明兼容只是針對設置沒有獲取 if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {//單選值的兼容 // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to default in case type is set after value during creation //當設置type = ‘radio‘時IE會有兼容性問題,所以要先設置類型才設置值 var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } }, propFix: { "for": "htmlFor", "class": "className" }, prop: function( elem, name, value ) { var ret, hooks, notxml, nType = elem.nodeType; // don‘t get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ];//兼容性處理 } if ( value !== undefined ) {//設置值 return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? ret : ( elem[ name ] = value );//prop使用的是.操作 } else {//獲取值 return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? ret : elem[ name ]; } }, propHooks: { tabIndex: {//光標切換順序,只對tabIndex屬性做兼容 get: function( elem ) {//只對get方法做兼容 return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? elem.tabIndex : -1; } } } }); // Hooks for boolean attributes boolHook = { set: function( elem, value, name ) { if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else { elem.setAttribute( name, name ); } return name; } }; jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) { var fn = jQuery.expr.attrHandle[ name ], ret = isXML ? undefined : /* jshint eqeqeq: false */ // Temporarily disable this handler to check existence (jQuery.expr.attrHandle[ name ] = undefined) != getter( elem, name, isXML ) ? name.toLowerCase() : null; // Restore handler jQuery.expr.attrHandle[ name ] = fn; return ret; }; }); // Support: IE9+ // Selectedness for an option in an optgroup can be inaccurate if ( !jQuery.support.optSelected ) { jQuery.propHooks.selected = { get: function( elem ) { var parent = elem.parentNode; if ( parent && parent.parentNode ) { parent.parentNode.selectedIndex; } return null; } }; } jQuery.each([ "tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable" ], function() { //value = callback.call( obj[ i ], i, obj[ i ] ); jQuery.propFix[ this.toLowerCase() ] = this; }); /* valHooks: { hooks = radio: { set: function( elem ) {} get: function( elem, value ) {} }, hooks = checkbox: { set: function( elem ) {}, get: function( elem, value ) {} } } */ jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { //$(‘#radio‘).val([‘hello‘]); set: function( elem, value ) { if ( jQuery.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );//設置選中狀態 } } }; if ( !jQuery.support.checkOn ) { //有的話做處理,沒有不做處理 //獲取單選框和復選框的value值時絕大多數瀏覽器返回的都是on,有些是空的, jQuery.valHooks[ this ].get = function( elem ) { // Support: Webkit // "" is returned instead of "on" if a value isn‘t specified return elem.getAttribute("value") === null ? "on" : elem.value; }; } });

jQuery07源碼 (3803 , 4299) attr() prop() val() addClass()等 : 對元素屬性的操作