关于钩子:http://www.cnblogs.com/aaronjs/p/3387906.html
本章的目的很简单,通过钩子函数更细节的了解浏览器差异与处理方案,
版本是2.0.3所以不兼容ie6.7.8,所以对应了钩子会少很多。。
总的来说钩子在.attr()
, .prop()
, .val()
and .css() 四种操作中会涉及
属性操作的钩子
propFix
propHooks
attrHooks
valHooks
jQuery.propFix 中的对象
源码部分
1:保留值属性名字修正
jQuery.propFix: {
for : "htmlFor",
class : "className"
},
- 由于class属于JavaScript保留值,因此当我们要操作元素的class属性值时,直接使用obj.getAttribute('class')和obj.setAttribute('class', 'value')可能会遭遇浏览器兼容性问题,W3C DOM标准为每个节点提供了一个可读写的className属性,作为节点class属性的映射,标准浏览器的都提供了这一属性的支持,因此,可以使用e.className访问元素的class属性值,也可对该属性进行重新斌值。而IE和Opera中也可使用e.getAttribute('className')和e.setAttribute('className', 'value')访问及修改class属性值。相比之下,e.className是W3C DOM标准,仍然是兼容性最强的解决办法。
- 同理htmlFor用于读取label标签的for属性
测试demo,通过class与className修改元素的属性
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<!-----------------------------测试代码开始-------------------------------->
<span id="test" for="mmm" in="nnn" disabled='disabled' class='ccc'>测试属性修改</span></br></br>
<span id="preserve1c71f7f89c0a405a87f520f3fe275bf0" class="wlWriterPreserve"><script type="text/javascript">
var test = document.getElementById('test');
document.write('原始class值结果class='+ test.className + '</br>')
test.class = 1111;
document.write('通过class修为1111结果class=' + test.className+ '</br>')
test.className = 2222;
document.write('通过className修为2222结果class=' + test.className+ '</br>')
</script></span>
<!-----------------------------测试结束-------------------------------------->
</body>
</html>
2:与表单操作相关:
反转下,让钩子适配用伪代码匹配,目测应该是为了兼容开发者输入大小写格式不正确
比如输入错误格式:cellpadding
如果jQuery.propFix 转成cellPadding ,驼峰写法了
jQuery.each([
"tabIndex",
"readOnly",
"maxLength",
"cellSpacing",
"cellPadding",
"rowSpan",
"colSpan",
"useMap",
"frameBorder",
"contentEditable"
], function() {
jQuery.propFix[ this.toLowerCase() ] = this;
});
tabIndex 属性可设置或返回按钮的 tab 键控制次序
readonly 属性规定输入字段为只读。
maxlength 属性规定输入字段的最大长度,以字符个数计。
cellspacing 属性规定单元格之间的空间
cellpadding 属性规定单元边沿与其内容之间的空白。
rowspan 属性规定单元格可横跨的行数。
colspan 属性规定单元格可横跨的列数。
HTML <img> 标签的
usemap 属性将图像定义为客户端图像映射
frameBorder 属性设置或返回是否显示框架周围的边框。
contenteditable 属性规定是否可编辑元素的内容。
值得一提的是这个方法用的比较巧妙了,收集所有的合集名,然后在每一个上下文回调中把每一个名字传递到propFix方法,key转成小写,value保存正确写法
jQuery.propFix[ this.toLowerCase() ] = this;
jQuery.propHooks 属性方法
关于tabIndex属性
http://www.w3help.org/zh-cn/causes/SD2021
propHooks: {
tabIndex: {
get: function( elem ) {
return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
elem.tabIndex :
-1;
}
}
}
// 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.attrHooks 方法
attrHooks: {
type: {
set: function( elem, value ) {
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
var val = elem.value;
elem.setAttribute( "type", value );
if ( val ) {
elem.value = val;
}
return value;
}
}
}
},
jQuery.valHooks 方法
根据 JQuery api文档 的描述,.val() 函数有两种用法,分别用来获取或设置元素的值,这里只介绍获取值的方法。
文档里面说 .val 主要是用于获取元素的value,比如 input, select 和 textarea等,
什么是元素的value?”
用 select 标签为例,如下的代码:
测试代码
<select id="choise">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
<option value="4">Four</option>
</select>
这里的option有2个值,一个是value = 1 另一个则是 text = One
option 真正的 value 应该是其 value 属性中的值,而不是 option 标签中间所包含的内容
在 w3school 的文档中对 option 的 value 属性的做了如下定义:
The value attribute specifies the value to be sent to a server when a form is submitted.
The content between the opening <option> and closing </option> tags is what the browsers will display in a drop-down list. However, the value of the value attribute is what will be sent to the server when a form is submitted.
Note: If the value attribute is not specified, the content will be passed as the value instead.
这一点对于所有可以拥有 value 属性的标签都是相同的,即在对一个元素调用 .val() 函数时,首先取其 value 属性的值,如果没有的话,再使用其 text 值。
那么接下来就要引入我们的valHooks,针对option,select的处理


option,select valHooks: {
option: {
get: function( elem ) {
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932
var val = elem.attributes.value;
return !val || val.specified ? elem.value : elem.text;
}
},
select: {
get: function( elem ) {
var value, option,
options = elem.options,
index = elem.selectedIndex,
one = elem.type === "select-one" || index < 0,
values = one ? null : [],
max = one ? index + 1 : options.length,
i = index < 0 ?
max :
one ? index : 0; // Loop through all the selected options
for ( ; i < max; i++ ) {
option = options[ i ]; // 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(); // We don't need an array for one selects
if ( one ) {
return value;
} // Multi-Selects return an array
values.push( value );
}
} return values;
}, set: function( elem, value ) {
var optionSet, option,
options = elem.options,
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;
}
}
},
对于val方法的取值部分
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;
} 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;
}
通过jQuery.valHooks匹配对应的钩子处理方法
节点属性的差异对比:
select : 创建单选或多选菜单
- type:"select-one"
- tagName: "SELECT"
- value: "111"
- textContent: "↵ Single↵ Single2↵"
option : 元素定义下拉列表中的一个选项
- tagName: "OPTION"
- value: "111"
- text: "Single"
- textContent: "Single"
radio : 表单中的单选按钮
- type: "radio"
- value: "11111"
checkbox : 选择框
- type: "checkbox"
- value: "11111"
根据对比select的节点type是'select-one’与其余几个还不同,所以jQuery在适配的时候采用优先查找type,否则就找nodeName的策略
hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
如果钩子匹配到了,并且还存在get方法,那么就要调用这个方法了,如果有返回值就返回当前的这个最终值
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
return ret;
}
那么具体的兼容问题就会跑到对应的钩子方法中处理了
// Loop through all the selected options
for ( ; i < max; i++ ) {
option = options[ i ]; // 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(); // We don't need an array for one selects
if ( one ) {
return value;
} // Multi-Selects return an array
values.push( value );
}
}
通过selectedIndex 属性可设置或返回下拉列表中被选选项的索引号
通过递归select中的所有option
在包装jQuery( option ).val()
从而调用option的钩子方法get ,
get: function( elem ) {
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932
var val = elem.attributes.value;
return !val || val.specified ? elem.value : elem.text;
}
elem.attributes.value返回对应的option的val值
所以这里就兼容的默认返回val,否则就返回text的内容了
radio,checkbox
// Radios and checkboxes getter/setter
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = {
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
}
}
};
if ( !jQuery.support.checkOn ) {
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;
};
}
});