你的位置:首页 > Java教程

[Java教程]第十章:属性模块


通常我们把对象的非函数成员叫属性。

对于元素节点来说,其属性大体分为两类固有属性自定义属性(特性)。固有属性一般遵循驼峰命名风格,拥有默认值,并且无法删除。

自定义属性是用户随意添加的属性值对,由于元素节点也是一个普通的javascript对象,没有什么严格的访问操作,因此命名风格林林总总,值的类型也是乱七八糟。但是随意添加属性显然不够安全,比如引起循环引用等,因此,浏览器提供了一组API来供人们操作自定义属性。

setAtttribute, getAttribute, removeAttribute。当然还有其他API。不过这是标准套装。只有在IE67等糟糕的环境下,我们才会求助其他的API、一般情况下此三个属性足也。

我们通常称其为DOM属性系统。DOM属性系统对属性名会进行小写化处理,属性值会统一转字符串。

  var el = document.createElement("div");  el.setAttribute("xxx","1");  el.setAttribute("XxX","2");  el.setAttribute("XXx","3");  console.log(el.getAttribute("xxx"))  console.log(el.getAttribute("XxX"))

IE6,IE7会返回1,其他浏览器返回3,在前端的世界里,我们走到哪都能碰到兼容性的问题。
IE67 在处理固有属性时要求进行名字映射,比如class变成className,for变成htmlFor。

对于布尔属性(一些返回布尔的属性),浏览器的差异更大。

<input type="radio" id="aaa"><input type="radio" checked id="bbb"><input type="radio" checked="checked" id="ccc"><input type="radio" checked="true" id="ddd"><input type="radio" checked="xxx" id="eee"><script type="text/javascript">"aaa,bbb,ccc,ddd,eee".replace(/\w+/g,function (id) {  var elem = document.getElementById(id)  console.log(elem.getAttribute("checked"))})</script>

IE9 FF15 chrome23以上分别为  null "" checked true xxx ,在ie 7 ie8差别太多,请自行尝试。

因此框架很有必要提供一些API来屏蔽这些差异性。但IE6时代,这个需求并不明显。因此,ie67不区分固有属性与自定义属性。 setAttribute和getAttribute在当时人们看来就是一个语法糖。用el.setAttribute("innerHTML","xxxx")与用el.innerHTML = "xxxx"效果一样,而且后者使用更方便。

即使早期使用广泛的prototype.js也提供的api也非常贫乏。

只有identify,readAttribute,writeAttribute,hasAttribute,className,hasClassName,addClassName,removeClassName toggleClassName与$F方法。

后来prototype.js也意识到固有属性和自定义属性之间的差异。在它的内部,搞了个Element._attributeTranslations

然而,在其内部还是优先el[name]方式来操作属性,而不是set/getAttribute。

jQuery早期的attr方法,其行为与prototype一模一样。只 不过jQuery1.6之前,是使用attr方法实现了读写删这三种操作。从易用性来说。不区分固有属性和自定义属性,由框架内部处理应该比attr,prop分家更容易接受。那么是什么致使jQuery这样做呢?答案是选择器引擎。

 

jQuery是最早以选择器为导向的类库。它最开始的选择器引擎是Xpath式,后来换成sizzle.以css表达式风格来选取元素,。在css.2.1引入了属性选择器[aaa=bbb],ie7也开始残缺支持。Sizzle毫不含糊地实现了这语法。属性选择器是最早突破类名与ID的限制求元素的。

css3时代,属性还能以[name^=value],[name*=value],[name$=value]等更精致的方式来甄选元素,而这一切都是建立在获取用户的预设值的基础上。因此jQuery下了很大的决心,把prop从attr切割出来。虽然为了满足用户的向前 兼容需求。又让attr做了prop的事。


浏览器经过了这么多年发展,谁也说不清元素节点有多少个属性。for.in循环也不行,因为它对不可遍历的属性无能为力。显式属性就是被显式设置的属性,分两种情况,一种是写在标签内的HTML属性,一种是通过setAttribute动态设置的属性。还是以el[xxx]=yyy的定义属性,还是没有定义的属性。可惜到ie8与其他浏览器中,你只看到寥寥可数特性节点。称为显式属性

在IE或其它浏览器,我们想判定一个属性是否为显式属性,直接用hasAttribute API判定

  var isSpecified = !"1"[0] ? function(el, attr) {    return el.hasAttribute(attr)  } : function(el, attr) {    var val = el.attributes[attr]    return !!val && val.specified  }

二.className的操作

我们操作一个属性通常只有三个选择:设置,读取,删除。但className有点特殊。它的值可以用空格隔开,分为多个类名。因此对类名的操作变成读取,添加,删除。在上代王者prototype.js,就已经把人们想要的类名操作总结出来。

我们操作一个属性通常只有三个选择:设置,读取,删除。但className有点特殊。它的值可以用空格隔开,分为多个类名。因此对类名的操作变成读取添加删除。在上代王者prototype.js,就已经把人们想要的类名操作总结出来。

  //prototype1.7  ClassName: function(element) {    return new Element.ClassNames(element);  },  hasClassName: function(element, className) {    if ((element = $(element))) return;    var elementClassName = element.className;    return (elementClassName.length >0 && (elementClassName == className || new RegExp("(^|\\s)" + className + "(s|$)").test(elementClassName)));  },  addClassName:function(element,className) {    if (!(element = $(element))) return;    if (!Element.hasClassName(element, className))      element.className += (element.className ? ' ' : '') + className;    return element;  },  removeClassName: function(element,className) {    if (!(element = $(element))) return;    element.className = element.className.replace(      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();    return element;  },  toggleClassName : function(element, className) {    if (!(element = $(element))) return;    return Element[Element.hasClassName(element, className) ? 'removeClassName' : 'addClassName'] (element,className)  },

另外,除了这些外,还提供了一个Element.ClassNames类,用于直接操作类名,这不禁把html5提供的classList联想起来。

prototype.js对className的操作已被认可,我们亦可以吧prototype.js精简一下,变成一些工具函数。在不引入类库时使用。

  var getClass = function(ele) {    return ele.className.replace(/\s+/," ").split(" ");  }  var hasClassName = function(ele,cls){    return -1 < (" "+ele.className+ " ").indexOf(" "+cls+" ");  }  var addClass = function(ele,cls) {    if (!this.hasClass(ele,cls)) {      ele.className += " "+ cls;    }  }  var removeClass = function(ele,cls) {    if (hasClass(ele,cls)){      var reg = new RegExp ('(\\s|^)'+cls+'(\\s|$)');      ele.className = ele.className.replace(reg," ");    }  }  var clearClass = function(ele, cls) {    ele.className = ""  }

 

三.jQuery的属性系统

希望分析早期的jQuery,带来一点启迪,jQuery系统经年累月,量变引发质变的结果。

  attr: function(elem, name, value) {    var fix = {      "for": "htmlFor",      "class": "className",      "float": "cssFloat",      innerHTML: "innerHTML",      className: "className",      value: "value",      disabled : "disabled"    };    if (fix[name]) {      if (value != undefined) elem[fix[name]] = value;      return elem[fix[name]];    } else if ( elem.getAttribute ) {      if (value != undefined ) elem.setAttribute( name, value);      return elem.getAttribute(name, 2);    } else {      name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});      if (value != undefined ) elem[name] = value;      return elem[name]    }  },

这个方法不断膨胀,加入prototype.js发掘出来的特殊属性处理,以及社区补丁。jquery1.5.2,这个attr方法已经接近100行。
在1.6时,把1.5版本css模块想出的好方法,cssHooks适配器移植过来,解决了扩展的难题

jquery1.6中存在4个适配器,从单词直接移过来,就是formHooksattrHooks,propHooks,valHooks。formHook是在attr方法对付旧版本的IE的form元素用到的。到jQuery1.61.新增一个boolHook对付布尔属性值。

到jquery1.6.3,人们发现ie大多数情况使用getAttributeNode,就能取到正确值,因此对formHook重构下。更名为nodeHooks,便形成今天的jQuery的属性系统。如图

 

上图为jQuery的属性系统概述图,新生的prop方法异常简单,复杂些都移动到钩子。
古老的attr方法则无比复杂,兼任读,写,删三职,由于IE的情况不得不动用到3个钩子(钩子只处理有问题的属性),不处理一般情况。

  //jQuery1.83  prop:function(elem, name, value) {    var ret, hooks, not elem.nodetype;    if ( !elem || nType === 3 || nType === 8 || nType === 2) {      return;//跳过注释节点,文本节点,特性节点    }    not= nType !== 1 || !jQuery.isif (not//如果是HTML文档的元素节点      name = jQuery.propFix[name] || name;      hooks = jQuery.propHooks[name];    }    if (value !== undefined) { //写方法      if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined) {        return ret; //处理特殊情况      } else { //处理通用情况        return (elem[name] = value );       }    } else { //读方法      if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) {      return ret;//处理特殊情况        } else { //处理通用情况        return elem[name];      }     }  } ,  attr: function (elem, name, value) {    var ret, hooks, not elem.nodeType;    if (!elem || nType === 3 || nType === 8 || nType === 2) {      return;// 跳过注释节点,文本节点,特性节点    }    if ( typeof elem.getAttribute === "undefined") {      return jQuery.prop(elem, name, value); //文档与window.只能使用prop    }    not= nType !== 1 || !jQuery.isif (not//如果是HTML文档元素节点      name = name.toLowerCase();//决定用哪一个钩子      hooks = jQuery.attrHooks[name] || (rboolean.test( name ) ? boolHook : nodeHook);    }    if (value !== undefined) {      if (value === null) { //模仿prototypejs移除属性        jQuery.removeAttr(elem, name);      } else if (hooks && notin hooks && (ret = hooks.set(elem, value, name)) !== undefined) {        return ret;// 处理特殊情况      } else { //处理通用        elem.setAttribute(name, value + "");        return value;      }    } else if (hooks && notin hooks && (ret = hooks.get(elem, name)) !== null) {      return ret;//处理特殊情况    } else { //处理通用情况      ret = elem.getAttribute( name );      return ret === null ? undefined : ret;    }  },

 (本文未完结,即将更新:)

四:value的操作

上一章: 第九章:样式模块  下一章:事件系统(onXXX等缺陷)




新疆旅游景点推荐新疆跟团旅游报价多少新疆旅游线路大全新疆旅游报价价格新疆旅游团购优惠2015年6月份有什么节日? 2015南京大屠杀纪念日是几月几日? 2015年暑假去哪儿旅游好?推荐国内五大最佳夏游地 夏至是什么意思?2015夏至是哪一天? 广州到东莞乐民游艺园多远?广州到水濂山乐民游艺园开车怎么走? 深圳到东莞乐民游艺园自驾怎么走?深圳到水濂山乐民游艺园开车多长时间? 深圳到东莞乐民游艺园有直达车吗?深圳到水濂山乐民游艺园怎么坐车? 水濂山乐民游艺园坐几路车?东莞乐民游艺园公交车路线? 流溪河音乐节2015要门票吗?从化流溪河音乐节门票价格? 2015雷公峡漂流团购多少钱?象头山雷公峡漂流门票多少钱? 2015雷公峡漂流五一有什么好玩的?惠州雷公峡漂流五一好玩吗? 2015雷公峡漂流开漂了吗?惠州雷公峡漂流开漂时间? 马尔代夫属于什么气候?一年有几个季节? 泰国的宗教信仰是什么? 迪拜的七星级酒店价格是多少? 泰国,有冬天吗?现在那边会冷吗? AT28HC256F-12DM/883 Datasheet AT28HC256F-12DM/883 Datasheet AT28HC256F-12DM/883DNU Datasheet AT28HC256F-12DM/883DNU Datasheet AT28HC256F-12FM/883 Datasheet AT28HC256F-12FM/883 Datasheet 大庆跟团港澳三日游 大庆跟团港澳三日游 大庆跟团港澳三日游 吉林去港澳5日游 吉林去港澳5日游 吉林去港澳5日游 牡丹江到港澳5日游 牡丹江到港澳5日游 牡丹江到港澳5日游