热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jQuery源码分析之removeAttr方法和attr方法

jQuery.removeAttr函数源码测试:<inputtyperadiochecked>varrnotwhite(\S+g);varp

jQuery.removeAttr函数源码测试:

var rnotwhite = (/\S+/g);	 var propFix ={		"for": "htmlFor",		"class": "className"	};	attrHandle = jQuery.expr.attrHandle;	ruseDefault = /^(?:checked|selected)$/i;	getSetAttribute =$. support.getSetAttribute;//通过setAttribute是否会影响property的访问,如果不影响返回true!	getSetInput = $.support.input;   jQuery.fn.extend({		removeAttr1: function( elem, value ) {		var name, propName,			i = 0,			//不要空格,如果传入一个以空格分割的字符串将他变成一个数组就可以了!			attrNames = value && value.match( rnotwhite ); 		if ( attrNames && elem.nodeType === 1 ) {			while ( (name = attrNames[i++]) ) {				propName = jQuery.propFix[ name ] || name;				// Boolean attributes get special treatment (#10870)				//对布尔值进行特殊处理!重置属性为false			///jQuery.expr.match.bool=^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i				if ( jQuery.expr.match.bool.test( name ) ) {					// Set corresponding property to false					//将input为radio的元素的checked设置false				 //如果是传入的"checked",那么就走else逻辑,在里面设置为elem["defaultChecked"]=elem["checked"]=false;					//也就是如果元素是"checked",那么经过else以后把checked变成false了!					if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {										elem[ propName ] = false;					// Support: IE<9					// Also clear defaultChecked/defaultSelected (if appropriate)					} else {						//支持IE<9把elem["defaultChecked"]=elem["checked"]=false;						elem[ jQuery.camelCase( "default-" + name ) ] =							elem[ propName ] = false;					}				// See #9699 for explanation of this approach (setting first, then removal)				//首先把相应的数据设置为空				} else {					jQuery.attr( elem, name, "" );				}                //支持get,setAttr方法就调用它				elem.removeAttribute( getSetAttribute ? name : propName );			}		}	}})$("input").eq(0).removeAttr1($("input")[0],"checked");//打印false,也就是说移除checked结果就是把checked属性变成false了!所以下面获取是falsealert($("input").get(0).checked);

note:

(1)对于设置的是boolean的属性,我们要特殊处理,但是还是通过下标的方式来处理,让他恢复默认值,然后调用removeAttribute!对于IE<9那么我就设置defaultChecked和defaultSelected大家可以看到这里对boolean类型的设置是通过下标的方式完成的,通过下标形式把boolean类型的全部设置为false!也就是恢复为默认状态,然后调用removeAttribute!

(2)removeAttr方法如果不是设置boolean类型的值,那么首先会把他设置为空字符串,然后再调用removeAttribute方法!但是调用removeAttribute的时候在不支持驼峰的时候直接删除name而不是propName,当时我在想,如果用户传入"removeAttr("contenteditable")"同时不支持驼峰属性的get/setAttribute最后就是直接调用的removeAttribute("contenteditable")但是html中属性是"cOntentEditable=true"啊,这样能移除吗?看下面的代码:

HTML部分:

JS部分:

//getAttribute可以大写也可以小写属性
$("#content")[0].removeAttribute("tabindex");
alert($("#content")[0].getAttribute("tabIndex"));//打印null,已经被移除了
//这表明对于removeAttribute来说:删除的属性既可以是驼峰写法也可以不是驼峰!
alert($("#content")[0].getAttribute("contentEditable"));//打印true
$("#content")[0].removeAttribute("contenteditable");
//打印null
alert($("#content")[0].getAttribute("contentEditable"));
所以,对于removeAttribute来说,移除的参数可以是驼峰写法也可以不是驼峰写法,get/setAttr也是一样的!(这是为什么在prop方法里面有相应的propFix而这里就不需要相应的Hooks把他转化为驼峰!)那么什么时候选择驼峰,什么时候选择非驼峰呢?因为getSetAttribute表示对Attribute的操作是否会影响property(通过className来判断的),如果不影响那么就是true,那么移除驼峰和非驼峰是没有影响的。如果对Attribute的操作会影响property,那么为了防止对property产生污染,那么我们必须移除驼峰写法,因为property就是驼峰的!

实例方法removeAttr源码:

removeAttr: function( name ) {
return this.each(function() {
jQuery.removeAttr( this, name );
});
}
removeAttr方法会对每一个调用对象调用jQuery.removeAttr方法!
实例attr方法源码(传入参数为调用对象,回调函数jQuery.attr,name,value是传入的参数,第四个参数是参数个数是否大于1,如果是读取属性那么value是undefined,同时第五个参数是false):读取属性的时候传入access里面的参数使得chainable,bulk都是false,length为true,所以直接执行代码fn(elems[0],key)

这使得attr方法在读取属性的时候回调函数为jQuery.attr方法的只是调用对象的第一个DOM元素和调用attr方法传入的key值,所以结果就是只是获取第一个元素的相应属性!但是在设置属性的时候确是对于调用对象的任何一个DOM元素!设置属性的值的时候如果传入第二个参数是函数,那么在access里面为每一个DOM执行jQuery.attr函数,传入的第一个参数是调用对象的DOM对象,第二个参数是attr方法key值,第三个参数是参数函数的执行结果!

attr: function( name, value ) {
return access( this, jQuery.attr, name, value, arguments.length > 1 );
}

var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
length = elems.length,
bulk = key == null;
// Sets many values
if ( jQuery.type( key ) === "object" ) {
chainable = true;
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
}
// Sets one value
} else if ( value !== undefined ) {
chainable = true;
if ( !jQuery.isFunction( value ) ) {
raw = true;
}
if ( bulk ) {
// Bulk operations run against the entire set
if ( raw ) {
fn.call( elems, value );
fn = null;

// ...except when executing function values
} else {
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
}
if ( fn ) {
for ( ; i //attr方法传入的第二个参数是函数时候,执行函数中this是DOM对象,第一个参数是下标,第二个参数是调用fn(elems[i],key)
//也就是获取该DOM元素的所有的以key为属性名的属性值。如attr("title",function(){})那么这里的第二个参数就是该DOM元素的
//所有title属性!所以传入的参数函数提供了对key参数对应的属性值的进一步操作,如更新,也起到了批量更新的作用!
//思路:先获取key的属性值+更新key对应属性值(通过进一步调用jQuery.attr方法完成!)
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
}

return chainable ?
elems :

// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
};

如果attr方法传入的第一个参数是一个对象,那么对每一个对象属性都调用一次access方法,同时这时候是赋值调用,对每一个attr方法的调用对象DOM都执行回调函数jQuery.attr方法,传入参数是DOM,key,value值,如{height:180,width:180}那么key第一次是height,第二次是widthjQuery.attr源码如下: 
 

attr: function( elem, name, value ) {
var hooks, ret,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === strundefined ) {
return jQuery.prop( elem, name, value );
}
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] ||
( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );//如果name在jQuery.attrHooks里面或者满足
//布尔正则那么返回boolHook
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
//没每一个Element设定值
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, value + "" );
return value;
}
//如果value是空那么表示获取值
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
//如果我们传入的是attr("checked")那么调用的是jQuery.find.attr也就是Sizzle的方法,然后返回一个checked字符串,不是boolean!
} else {//对于prop还说,他是通过elem["xx"]方式访问的,对于表单元素,只要他有checked那么他的值就是true,所以返回true!
ret = jQuery.find.attr( elem, name );
// Non-existent attributes return null, we normalize to undefined
return ret == null ?
undefined :
ret;
}
}

测试代码:

//通过attr添加是小写,获取驼峰的时候也是小写!
//打印"5"
alert($("#content").attr("tabIndex"));
//打印"5",表示获取的驼峰属性的时候属性本身可以是驼峰,也可以不是!
alert($("#content").attr("tabIndex"));
//调用removeAttr的时候如果是小写或者大写都是可以的!这是为了兼容有些人喜欢小写属性有些人喜欢驼峰!
$("#content").removeAttr("tabIndex");
//打印undefined
alert($("#content").attr("tabindex"));
 
 

对应的attrHooks:

attrHooks: {
type: {
set: function( elem, value ) {
if ( !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
var val = elem.value;
elem.setAttribute( "type", value );
if ( val ) {
elem.value = val;
}
return value;
}
}
}
}

boolHook:对于boolean值,如checked|selected|async|autofocus|autoplay|controls等设置值也是经过特殊的处理的,因为我们的attr调用boolean的值的时候也是返回的字符串,而不是boolean值。对于获取值,boolHook里面没有get方法,所以对于获取值还是通过jQuery.find.attr来完成的,该方法会返回相应的字符串!但是对于设置值,那么如果value是false那么就移除该属性;否则修改defaultChect等为true,这一修改下次调用的时候就会依赖jQueyr.find.attr从而获取到"checkde"字符串而不是布尔值。(注意:这里即使调用attr("checked","xxx")也会使得下次获取到"checked"

// Hook for boolean attributes
boolHook = {
set: function( elem, value, name ) {
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );//attr("checked",false)调用将会把checked的属性变成"undefined"
} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
// IE<8 needs the *property* name //对于getAttribute获取不到正确的值的情况,或者修改attribute会修改property的情况
//那么我们设置setAttribute("checked","checked")这种类型
elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
// Use defaultChecked and defaultSelected for oldIE
} else {//那么就是把这个元素的defaultChecked,defaultSelected设置为true。调用attr("checked","xxxx")
//就会把defaultChcked设置为true了,那么再次获取通过getAttribute获取时候的返回值就是"checked"从而转化为字符串!
//但是同时设置了elem[name]=true,所以通过property获取还是布尔值!
elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
}
return name;
}
};

contentEditable:如果把contentEditable设置为false表示不让编辑了,如果设置为空字符串那么会抛出异常!

jQuery.attrHooks.cOntenteditable= {
set: function( elem, value, name ) {
nodeHook.set( elem, value === "" ? false : value, name );
}
};
width/height:如果我们调用attr方法设置width/height的时候设置为空字符串,那么jQuery会把height/width设置为auto!

jQuery.each([ "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = {
set: function( elem, value ) {
if ( value === "" ) {
elem.setAttribute( name, "auto" );
return value;
}
}
};
});

下面是对于style的特殊处理:

// Get the style information from getAttribute
// (IE uses .cssText instead)
a.style.cssText = "top:1px";
support.style = /top/.test( a.getAttribute("style") );
所以,如果support.style返回true,表示我们可以通过getAttribute获取到style属性中的text,如果是false表示getAttribute不能用于style!如果不能用于style就要做特殊的处理!处理逻辑如下:其目地就是为了兼容IE<8,因为IE<8通过getAttribute获取style返回的是对象,所以针对这种情况我们要用elem.style.cssText来特殊处理!

if ( !support.style ) {
jQuery.attrHooks.style = {
get: function( elem ) {
// Return undefined in the case of empty string
// Note: IE uppercases css property names, but if we were to .toLowerCase()
// .cssText, that would destroy case senstitivity in URL's, like in "background"
return elem.style.cssText || undefined;
},
set: function( elem, value ) {
return ( elem.style.cssText = value + "" );
}
};
}
value:

getSetInput检测是否可以信任getAttribute("value"),getSetAttribute检测用setAttribute修改属性是否会修改property属性!

    getSetInput = support.input;
// Support: IE8 only
// Check if we can trust getAttribute("value")
input = document.createElement( "input" );
input.setAttribute( "value", "" );
support.input = input.getAttribute( "value" ) === "";//如果getSetInput为true表示我们可以信任get/setAttribute!

在老版本的IE中,如果get/setAttribute不可信或者通过setAttribute修改属性会修改property属性,那么对value的值的设置也进行特殊的处理!也就是如果是设置input的值,那么修改defaultValue,如果不是修改input,那么调用IE6/7的nodeHook!

// fix oldIE attroperties
if ( !getSetInput || !getSetAttribute ) {
jQuery.attrHooks.value = {
set: function( elem, value, name ) {
if ( jQuery.nodeName( elem, "input" ) ) {
// Does not return so that setAttribute is also used
elem.defaultValue = value;//设置value值就是回归defaultValue!
} else {
// Use nodeHook if defined (#1954); otherwise setAttribute is fine
return nodeHook && nodeHook.set( elem, value, name );
}
}
};
}

getSetAttribute检测:

	div = document.createElement( "div" );
div.setAttribute( "className", "t" );
// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
support.getSetAttribute = div.className !== "t";
在IE6/IE7中,如果setAttribute设置了className那么property也修改了!对于这种情况jQuery也做了兼容。在IE6/7中,设置属性的值的时候,首先调用getAttributeNode获取到属性节点,如果该属性节点不存在,那么创建一个属性节点赋值到该元素上面,但是这时候是一个空的属性节点;第二步就是把value值赋值到上一步创建的属性节点上。

nodeHooks:(setContentEditable中调用为:nodeHook.set(elem,"aa"/false,"contentEditable"))

// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !getSetAttribute ) {
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
nodeHook = {
set: function( elem, value, name ) {
// Set the existing or create a new attribute node
var ret = elem.getAttributeNode( name );
if ( !ret ) {
elem.setAttributeNode(
(ret = elem.ownerDocument.createAttribute( name ))
);
}
ret.value = value += "";//如果设置的值是value那么返回设置的值,否则通过getAttribute查询后返回value值!
// Break association with cloned elements by also using setAttribute (#9646)
if ( name === "value" || value === elem.getAttribute( name ) ) {
return value;//第一个name==="value"表示上面对于value也进行了hook!
}
}
};
总结:

(1)对于布尔类型的表单元素,如attr("checked")和prop("checked")前者调用了jQuery.find.attr也就是Sizzle的attr方法,最后返回的是一个字符串,如"checked"。对于prop来说,他最后调用的是elem["checked"],只要表单元素有checked,那么他的值就是true,是一个布尔值!当然,如果要获取字符串,我们自己也可以调用jQuery.find.attr(elem,"checked"),要获取布尔值,原生的JS可以用elem.checked!

(2)IE<8通过getAttribute获取style返回的是一个对象,但是我们通过attr("style")获取的时候却获取到了其中的字符串,所以jQuery对我们这种情况做了处理,也就是说对于这种情况,如果浏览器获取到的style是对象,也就是IE<8,那么我们就自己处理,通过style.cssText获取,否则我们还是调用jQuery.find.attr来获取。所以还是为了兼容IE<8的浏览器的!对于prop方法不要做任何兼容,直接调用elem["style"]就可以了,他返回的是对象!

(3)如果我们调用attr方法设置width/height的时候设置为空字符串,那么jQuery会把height/width设置为auto!

(4)调用attr方法设置的时候或者获取的时候都会把参数进行小写化,然后调用相应的方法如jQuery.find.attr,因为该方法是获取小写的参数,所以如果你有一个属性是"classMe"那么你可以传入attr方法参数是"classme/classMe"任何一种,这是不是可浏览器原生的get/setAttribute一致了呢。


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文详细介绍了使用C#实现Word模版打印的方案。包括添加COM引用、新建Word操作类、开启Word进程、加载模版文件等步骤。通过该方案可以实现C#对Word文档的打印功能。 ... [详细]
author-avatar
U友47919166
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有