在jQuery之attr源码解析中,我们讲解了jquery对于attr方法的兼容,通过hooks方式的架构方式进行兼容处理,这种兼容方式有很好的可扩展性,很值得我们学习,今天我们就对具体的attr的hooks的兼容处理之一:input属性兼容,进行源码分析。
我们先上源码
attrHooks: { type: { set: function( elem, value ) { // We can't allow the type property to be changed(since it causes problems in IE) if ( rtype.test( elem.nodeName ) && elem.parentNode ) { jQuery.error( "type property can't be changed" ); }else 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 it's default in case type is set after value // This is for element creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } }, // Use the value property for back compat // Use the nodeHook for button elements in IE6/7 (#1954) value: { get: function( elem, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.get( elem, name ); } return name in elem ? elem.value : null; }, set: function( elem, value, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.set( elem, value, name ); } // Does not return so that setAttribute is also used elem.value = value; } } },
首先,type包含set的兼容处理:
type: { set: function( elem, value ) { // We can't allow the type property to be changed (since it causes problems in IE) if ( rtype.test( elem.nodeName ) && elem.parentNode ) { jQuery.error( "type property can't be changed" ); } else 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 it's default in case type is set after value // This is for element creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } }
rtype = /^(?:button|input)$/i,
jQuery.extend({ nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); } });
input = document.createElement("input"); input.value = "t"; input.setAttribute( "type", "radio" ); support.radioValue = input.value === "t";
第一句“rtype.test(elem.nodeName)&&parentNode”,这句话表示的意思是什么呢?“rtype”是上面的正则,表示标签名是“input”或者“button”,parentNode代表元素的父元素,什么元素有父元素呢,中文档中的元素有父元素,通过“document.createElement('input')”创建的元素没有父元素。
这个判断就是说,如果是文档中的input元素要修改type类型,那么对不起,由于IE的原因,jquery不支持。
而下面的else if中的判断又代表什么呢?
这个support.radioValue在chrome,Firfox浏览器下为true,在IE浏览器下为false;这个为什么IE6-9下为什么会是false呢,jquery给出了注释,因为做IE6-9下面,
"radio"元素创建过程中,需要先设置“type”类型,后设置“value”,如果先设置“value”,后设置“type”,那么在设置“type”的时候,会重置为默认的“on”值。而jquery采取的input的创建方式是先设置“value”后设置“type”的,所以要做这个处理,也可以说这个兼容性是jquery自个搞出来的。
if里面的代码就好理解了,就是先保存value值,然后设置type,之后再恢复value值。在这个版本中attr("type","radio")只是jquery内部使用创建元素的功能。
上面的源码摘自1.8.3版本的jquery,
而在jquery高版本(我看的是 1.9.1版本)中的type兼容代码如下:
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; } } } }
新版本jquery中去除了对是否是文档中元素修改“type”的判断,反正对于IE浏览器,你修改type都会报错,干脆去除那些个多余的判断,反倒能使chrome,friefox等浏览器可以修改type类型。这个改进是很有必要的,因为我们现在很多系统页面有时候会不考虑IE浏览器,jquery这一改进,是不能因为IE得安全限制策略而抛弃了其他浏览器的可用功能。
type属性这有set的兼容,get获取,走的还是“getAttribute”或者“getAttributeNode”常规方法来获取属性值。
下面我们再看看value属性的兼容代码
// Use the value property for back compat // Use the nodeHook for button elements in IE6/7 (#1954) value: { get: function( elem, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.get( elem, name ); } return name in elem ? elem.value : null; }, set: function( elem, value, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.set( elem, value, name ); } // Does not return so that setAttribute is also used elem.value = value; } }
这里提一点,你可以细看前文中对于attr方法的源码解析,hooks方法如果返回的是undefined,那么他会继续调用通过的“getAttribute”,"setAttribute"等方法处理。
value属性有两个兼容方法“get”和“set”。先看get方法
get: function( elem, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.get( elem, name ); } return name in elem ? elem.value : null; },
if ( !getSetAttribute ) { fixSpecified = { name: true, id: true, coords: true }; // Use this for any attribute in IE6/7 // This fixes almost every IE6/7 issue nodeHook = jQuery.valHooks.button = { get: function( elem, name ) { var ret; ret = elem.getAttributeNode( name ); return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? ret.value : undefined; }, set: function( elem, value, name ) { // Set the existing or create a new attribute node var ret = elem.getAttributeNode( name ); if ( !ret ) { ret = document.createAttribute( name ); elem.setAttributeNode( ret ); } return ( ret.value = value + "" ); } }; }
div = document.createElement("div"); // Setup div.setAttribute( "className", "t" ); getSetAttribute: div.className !== "t",
get方法中,先判断nodeHook是否是undefined,如果不是undefined,那么就调用nodeHook的get方法获取value属性。
nodeHook是什么呢,通过判断setAttribute能否添加属性成功来判断setAttribute的可用性,IE6-7虽然也有setAttribute方法,但是调用无效,需要使用setAttributeNode方法来设置属性。nodeHook就是通过setAttributeNode方法设置属性,通过getAttributeNode获取属性的。
nodeHook中的get方法判断属性节点不为空并且是“name”,“id”,"coords"属性之一或者属性节点值不为空字符串时返回属性节点值,否则返回undefined。
非IE6-7浏览器下,value属性的get方法返回属性值或者null。对于自定义属性返回null会促使调用“getAttribute”方法来获取属性值。
set: function( elem, value, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.set( elem, value, name ); } // Does not return so that setAttribute is also used elem.value = value; }
value的set方法和get方法的逻辑是一样的,在非IE6-7浏览器下通过“elem.value=value”不设置返回值,促使调用“setAttribute”来设置自定义属性。
本文讲解的jquery的hooks中的type和value兼容处理的东西不多,type就是处理radio的赋值先后在IE下的问题,并且主要是jquery内部创建爱元素使用,value兼容也是兼容IE6-7下的setAttribute,getAttribute不起作用的问题。
希望通过对jquery源码的分析,能让你掌握更多浏览器兼容性的知识,虽然IE有时我们不再考虑,但是大部分情况下,IE还是占很大份额的,毕竟是随系统安装的浏览器,用户不再少量,再加之各种国产浏览器,都是IE和webkit的双内核浏览器,IE的兼容在PC端就更不能弃之不理了。