前文 对属性的设置、读取、删除方法做了分解,本文继续对jQuery attributes模块分解。
jQuery.fn.addClass
/* *********************************** * value: 字符串或者是函数,字符串可以通过空格分隔className */ jQuery.fn.addClass = function ( value ) { var classes, elem, cur, clazz, j, i = 0 , len = this .length, proceed = typeof value === "string" && value; // 如果value是函数 if ( jQuery.isFunction( value ) ) { // 则对所有元素迭代运行addClass return this .each( function ( j ) { jQuery( this ).addClass( value.call( this , j, this .className ) ); }); } // 如果value是字符串 if ( proceed ) { // 对value字符串分割成数组 classes = ( value || "" ).match( core_rnotwhite ) || []; // 遍历元素 for ( ; i < len; i++ ) { elem = this [ i ]; // 如果节点是元素,则获取原来的className cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : // 替换掉换行符制表符等 " " ); // 如果cur不为false,即节点是元素 if ( cur ) { j = 0 ; // 遍历classes组装成新的className应有的值 while ( (clazz = classes[j++ ]) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " " ; } } // 对className赋值,去掉头尾空白 elem.className = jQuery.trim( cur ); } } } return this ; };
添加class的实现上还是比较简单的,利用elem.className来赋值。需要注意:
var rclass = /[\t\r\n]/g;
jQuery.fn.removeClass
jQuery.fn.removeClass = function ( value ) { var classes, elem, cur, clazz, j, i = 0 , len = this .length, // 参数是否正确 proceed = arguments.length === 0 || typeof value === "string" && value; // 如果value是函数 if ( jQuery.isFunction( value ) ) { // 则对所有元素迭代运行removeClass return this .each( function ( j ) { jQuery( this ).removeClass( value.call( this , j, this .className ) ); }); } // 如果参数正确 if ( proceed ) { // 分隔value成为class字符串数组 classes = ( value || "" ).match( core_rnotwhite ) || []; // 遍历 for ( ; i < len; i++ ) { elem = this [ i ]; // 获取className并进行预处理 cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : "" ); // 如果是元素 if ( cur ) { j = 0 ; // 遍历所有class字符串 while ( (clazz = classes[j++ ]) ) { // 寻找是否有对应的字符串 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { // 有则去掉 cur = cur.replace( " " + clazz + " ", " " ); } } // 给className赋值,并去掉头尾空格 elem.className = value ? jQuery.trim( cur ) : "" ; } } } return this ; };
删除class的实现和addClass非常像,只是通过indexOf和replace来替换掉需要删除的class。
jQuery.fn.toggleClass
jQuery.fn.toggleClass = function ( value, stateVal ) { var type = typeof value, isBool = typeof stateVal === "boolean" ; // (⊙o⊙)…不说了,大家懂得 if ( jQuery.isFunction( value ) ) { return this .each( function ( i ) { jQuery( this ).toggleClass( value.call( this , i, this .className, stateVal), stateVal ); }); } // 遍历所有元素 return this .each( function () { // 如果value是字符串 if ( type === "string" ) { var className, i = 0 , self = jQuery( this ), state = stateVal, // 将value转成classNames字符串数组 classNames = value.match( core_rnotwhite ) || []; // 遍历 while ( (className = classNames[ i++ ]) ) { // stateVal是布尔量,则直接设置为stateVal,否则判断元素是否不存在该className state = isBool ? state : ! self.hasClass( className ); // 如果该className不存在则添加,否则删除 self[ state ? "addClass" : "removeClass" ]( className ); } // 如果value的类型是undefined或者boolean } else if ( type === "undefined" || type === "boolean" ) { // 如果元素的className存在 if ( this .className ) { // 将其存入缓存 jQuery._data( this , "__className__", this .className ); } // 对className赋值,为空或者缓存中的值 this .className = this .className || value === false ? "" : jQuery._data( this , "__className__" ) || "" ; } }); };
为了实现jQuery.fn.toggleClass还是花了很大功夫的。
缓存的利用使得toggleClass操作更加方便,而不需要记录以前是那些class。
jQuery.fn.hasClass
jQuery.fn.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 ; };
这个函数通过indexOf来寻找className是否存在。
jQuery.fn.val
jQuery.fn.val = function ( value ) { var hooks, ret, isFunction, elem = this [0 ]; // 如果没有参数 if ( ! arguments.length ) { // 如果元素存在 if ( elem ) { // 得到相应的钩子 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; // 通过钩子来得到值 if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; } // 如果没得到钩子,则通过elem.value来返回值 ret = elem.value; // 如果ret是字符串 return typeof ret === "string" ? // 将回车符替换 ret.replace(rreturn, "" ) : // 如果ret是空的,则返回"",否则返回ret ret == null ? "" : ret; } return ; } // value是否是函数 isFunction = jQuery.isFunction( value ); // 遍历所有元素 return this .each( function ( i ) { var val, self = jQuery( this ); if ( this .nodeType !== 1 ) { return ; } // 如果value是函数,则转成参数 if ( isFunction ) { val = value.call( this , i, self.val() ); } else { val = value; } // 将null/undefined当成"" if ( val == null ) { val = "" ; // 将数字转成字符串 } else if ( typeof val === "number" ) { val += "" ; // 如果是数组,则遍历数组 } else if ( jQuery.isArray( val ) ) { val = jQuery.map(val, function ( value ) { return value == null ? "" : value + "" ; }); } // 获取相应钩子 hooks = jQuery.valHooks[ this .type ] || jQuery.valHooks[ this .nodeName.toLowerCase() ]; // 如果钩子无法设置,则使用通常的设置方法 if ( !hooks || !("set" in hooks) || hooks.set( this , val, "value" ) === undefined ) { this .value = val; } }); };