好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

mass Framework attr模块 v3

mass Framework attr模块 v3

mass Framework attr模块 v3

为了获取最佳的性能与兼容性,attr模块在v3中分割为两大块。attr模块大胆HTML5的classList,Array.isArray等新式API, attr_fix则是专门为IE6789准备的。attr模块存在大量的钩子,为摆平浏览器做了许多工件,但我们没有必须把所有补丁都集成到一个JS文件,其中过半是为IE侍侯的,因此分割出去对标准浏览器的加载量非常有利。

下面是attr.js,它相当于jQuery的attributes.js(github中的划分)。

?

//==================================================

// 属性操作模块 v3

//==================================================

define( "attr" ,!!top.getComputedStyle ? [ "$node" ] : [ "$attr_fix" ], function ( $ ){

     var   rreturn = /\r/g,

     rtabindex = /^(a|area|button|input|object|select|textarea)$/i,

     rnospaces = /\S+/g,

     support = $.support

     function   getValType( el ){

         var   ret = el.tagName.toLowerCase();

         return   ret == "input"   && /checkbox|radio/.test(el.type) ? el.type : ret;

     }

 

     $.implement({

         /**

          *  为所有匹配的元素节点添加className,添加多个className要用空白隔开

          *  如$("body").addClass("aaa");$("body").addClass("aaa bbb");

          *  <a href=" http://www.cnblogs.com/rubylouvre/archive/2011/01/27/1946397.html ">相关链接</a>

          */

         addClass: function ( item ){

             if   ( typeof   item == "string" ) {

                 for   ( var   i = 0, el; el = this [i++]; ) {

                     if   ( el.nodeType === 1 ) {

                         item.replace(rnospaces, function (clazz){

                             el.classList.add(clazz);

                         })

                     }

                 }

             }

             return   this ;

         },

         //如果不传入类名,则清空所有类名,允许同时删除多个类名

         removeClass: function ( item ) {

             var   removeSome  = item && typeof   item === "string" ,removeAll = item === void 0

             for   ( var   i = 0, node; node = this [ i++ ]; ) {

                 if   ( node.nodeType === 1 ) {

                     if (removeSome && node.className){

                         item.replace(rnospaces, function (clazz){

                             node.classList.remove(clazz);

                         })

                     } else   if (removeAll){

                         node.className = "" ;

                     }

                 }

             }

             return   this ;

         },

         

         //如果第二个参数为true,要求所有匹配元素都拥有此类名才返回true

         hasClass: function ( item, every ) {

             var   method = every === true   ? "every"   : "some" ,

             rclass = new   RegExp( '(\\s|^)' +item+ '(\\s|$)' ); //判定多个元素,正则比indexOf快点

             return   $.slice( this )[ method ]( function ( el ){ //先转换为数组

                 return    (el.className || "" ).match(rclass);

             });

         },

         //如果存在(不存在)就删除(添加)指定的类名。对所有匹配元素进行操作。

         toggleClass: function ( value, stateVal ){

             var   type = typeof   value , classNames = type === "string"   && value.match( rnospaces ) || [], className, i,

             isBool = typeof   stateVal === "boolean" ;

             return   this .each( function ( el ) {

                 i = 0;

                 if (el.nodeType === 1){

                     var   self = $( el ),

                     state = stateVal;

                     if (type == "string"   ){

                         while   ( (className = classNames[ i++ ]) ) {

                             state = isBool ? state : !self.hasClass( className );

                             self[ state ? "addClass"   : "removeClass"   ]( className );

                         }

                     } else   if   ( type === "undefined"   || type === "boolean"   ) {

                         if   ( el.className ) {

                             $._data( el, "__className__" , el.className );

                         }

                         el.className = el.className || value === false   ? ""   : $._data( el, "__className__" ) || "" ;

                     }

                 }

             });

         },

         //如果匹配元素存在类名old则将其置换为类名neo

         replaceClass: function ( old, neo ){

             for   ( var   i = 0, node; node = this [ i++ ]; ) {

                 if   ( node.nodeType === 1 && node.className ) {

                     var   arr = node.className.match( rnospaces ), arr2 = [];

                     for   ( var   j = 0; j < arr.length; j++ ) {

                         arr2.push( arr[j] == old ? neo : arr[j]);

                     }

                     node.className = arr2.join( " " );

                 }

             }

             return   this ;

         },

         val : function ( item ) {

             var   el = this [0], getter = valHooks[ "option:get"   ];

             if   ( !arguments.length ) { //读操作

                 if   ( el && el.nodeType == 1 ) {

                     var   ret =  (valHooks[ getValType(el)+ ":get"   ] ||

                         $.propHooks[ "@default:get"   ])( el, "value" , getter );

                     return    typeof   ret === "string"   ? ret.replace( rreturn, ""   ) : ret == null   ? ""   : ret;

                 }

                 return   void 0;

             }

             //我们确保传参为字符串数组或字符串,null/undefined强制转换为"", number变为字符串

             if ( Array.isArray( item ) ){

                 item = item.map( function   (item) {

                     return   item == null   ? ""   : item + "" ;

                 });

             } else   if ( isFinite(item) ){

                 item += "" ;

             } else {

                 item = item || "" ;

             }

             return   this .each( function ( el ) { //写操作

                 if   ( el.nodeType == 1 ) {

                     (valHooks[ getValType(el)+ ":set"   ] ||

                         $.propHooks[ "@default:set"   ])( el, "value" , item , getter );

                 }

             });

         }

     });

    

     var   cacheProp = {}

     function   defaultProp(node, prop){

         var   name = node.tagName+ ":" +prop;

         if (name in   cacheProp){

             return   cacheProp[name]

         }

         return   cacheProp[name] = document.createElement(node.tagName)[prop]

     }

     $.mix({

         fixDefault: $.noop,

         propMap:{ //属性名映射

             "accept-charset" : "acceptCharset" ,

             "char" : "ch" ,

             "charoff" : "chOff" ,

             "class" : "className" ,

             "for" : "htmlFor" ,

             "http-equiv" : "httpEquiv"

         },

         prop: function (node, name, value){

             if ($[ "@bind" ] in   node){

                 if (node.nodeType === 1 && !$.isXML( node )){

                     name = $.propMap[ name.toLowerCase() ] || name;

                 }

                 var   access = value === void 0 ? "get"   : "set"

                 return   ($.propHooks[name+ ":" +access] ||

                     $.propHooks[ "@default:" +access] )(node, name, value)

             }

         },

         attr: function (node, name, value){

             if ($[ "@bind" ] in   node){

                 if   ( typeof   node.getAttribute === "undefined"   ) {

                     return   $.prop( node, name, value );

                 }

                 //这里只剩下元素节点

                 var   noxml = !$.isXML( node ), type = "@w3c" , isBool

                 if ( noxml ){

                     name = name.toLowerCase();

                     var   prop = $.propMap[ name ] || name

                     if ( !support.attrInnateName ){

                         type = "@ie"

                     }

                     isBool = typeof   node[ prop ] == "boolean"   && typeof   defaultProp(node,prop) == "boolean" //判定是否为布尔属性

                 }

                 //移除操作

                 if (noxml){

                     if   (value === null   || value === false   && isBool ){

                         return   $.removeAttr(node, name )

                     }

                 } else   if ( value === null   ) {

                     return   node.removeAttribute(name)

                 }

                 //读写操作

                 var   access = value === void 0 ? "get"   : "set"

                 if ( isBool ){

                     type = "@bool" ;

                     name = prop;

                 }

                 return   ( noxml  && $.attrHooks[ name+ ":" +access ] ||

                     $.attrHooks[ type + ":" +access] )(node, name, value)

             }

         },

         //只能用于HTML,元素节点的内建不能删除(chrome真的能删除,会引发灾难性后果),使用默认值覆盖

         removeProp: function ( node, name ) {

             if (node.nodeType === 1){

                 if (!support.attrInnateName){

                     name = $.propMap[ name.toLowerCase() ] ||  name;

                 }

                 node[name] = defaultProp(node, name)

             } else {

                 node[name] = void 0;

             }

         },

         //只能用于HTML

         removeAttr: function ( node, name ) {

             if (name && node.nodeType === 1){

                 name = name.toLowerCase();

                 if (!support.attrInnateName){

                     name = $.propMap[ name ] ||  name;

                 }

                 //小心contentEditable,会把用户编辑的内容清空

                 if ( typeof   node[ name ] != "boolean" ){

                     node.setAttribute( name, "" )

                 }

                 node.removeAttribute( name );

                 // 确保bool属性的值为bool

                 if   ( node[ name ] === true   ) {

                     node[ name ] = false ;

                     $.fixDefault(node, name, false )

                 }

             }

         },

         propHooks:{

             "@default:get" : function ( node, name ){

                 return   node[ name ]

             },

             "@default:set" : function (node, name, value){

                 node[ name ] = value;

             },

             "tabIndex:get" : function ( node ) {

                 // http://www.cnblogs.com/rubylouvre/archive/2009/12/07/1618182.html

                 var   ret = node.tabIndex;

                 if ( ret === 0 ){ //在标准浏览器下,不显式设置时,表单元素与链接默认为0,普通元素为-1

                     ret = rtabindex.test(node.nodeName) ? 0 : -1

                 }

                 return   ret;

             }

         },

         attrHooks: {

             "@w3c:get" : function ( node, name ){

                 var   ret =  node.getAttribute( name ) ;

                 return   ret == null   ? void 0 : ret;

             },

             "@w3c:set" : function ( node, name, value ){

                 node.setAttribute( name, ""   + value )

             },

             "@bool:get" : function (node, name){

                 //布尔属性在IE6-8的标签大部字母大写,没有赋值,并且无法通过其他手段获得用户的原始设值

                 return   node[ name ] ? name.toLowerCase() : void 0

             },

             "@bool:set" : function (node, name){

                 //布尔属性在IE6-8的标签大部字母大写,没有赋值,并且无法通过其他手段获得用户的原始设值

                 node.setAttribute( name, name.toLowerCase() )

                 node[ name ]  = true ;

                 $.fixDefault(node, name, true )

             }

 

         }

     });

     "Attr,Prop" .replace($.rword, function ( method ){

         $.fn[ method.toLowerCase() ] = function ( name, value ) {

             return   $.access( this , name, value, $[ method.toLowerCase() ] );

         }

         $.fn[ "remove"   + method] = function (name){

             return   this .each( function () {

                 $[ "remove"   + method]( this , name );

             });

         }

     });

     //========================propHooks 的相关修正==========================

     var   prop = "accessKey,allowTransparency,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan,contentEditable," +

     "dateTime,defaultChecked,defaultSelected,defaultValue,frameBorder,isMap,longDesc,maxLength,marginWidth,marginHeight," +

     "noHref,noResize,noShade,readOnly,rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" ;

     prop.replace($.rword, function (name){

         $.propMap[name.toLowerCase()] = name;

     });

 

     //safari IE9 IE8 我们必须访问上一级元素时,才能获取这个值

     if   ( !support.optSelected ) {

         $.propHooks[ "selected:get"   ] = function ( node ) {

             for ( var   p = node; typeof   p.selectedIndex != "number" ;p = p.parentNode){}

             return   node.selected;

         }

     }

     //========================valHooks 的相关修正==========================

     var   valHooks = {

         "option:get" :  function ( node ) {

             var   val = node.attributes.value;

             //黑莓手机4.7下val会返回undefined,但我们依然可用node.value取值

             return   !val || val.specified ? node.value : node.text;

         },

         "select:get" : function ( node ,value, getter) {

             var   option,  options = node.options,

             index = node.selectedIndex,

             one = node.type === "select-one"   || index < 0,

             values = one ? null   : [],

             max = one ? index + 1 : options.length,

             i = index < 0 ? max :  one ? index : 0;

             for   ( ; i < max; i++ ) {

                 option = options[ i ];

                 //旧式IE在reset后不会改变selected,需要改用i === index判定

                 //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable

                 //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况

                 if   ( ( option.selected || i === index ) &&

                     (support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&

                     (!option.parentNode.disabled || !$.type( option.parentNode, "OPTGROUP"   )) ) {

                     value = getter( option );

                     if   ( one ) {

                         return   value;

                     }

                     //收集所有selected值组成数组返回

                     values.push( value );

                 }

             }

             return   values;

         },

         "select:set" : function ( node, name, values, getter ) {

             $.slice(node.options).forEach( function ( el ){

                 el.selected = !!~values.indexOf( getter(el) );

             });

             if   ( !values.length ) {

                 node.selectedIndex = -1;

             }

         }

     }

     //checkbox的value默认为on,唯有chrome 返回空字符串

     if   ( !support.checkOn ) {

         "radio,checkbox" .replace( $.rword, function ( name ) {

             valHooks[ name + ":get"   ] = function ( node ) {

                 return   node.getAttribute( "value" ) === null   ? "on"   : node.value;

             }

         });

     }

     //处理单选框,复选框在设值后checked的值

     "radio,checkbox" .replace( $.rword, function ( name ) {

         valHooks[ name + ":set"   ] = function ( node, name, value) {

             if   ( Array.isArray( value ) ) {

                 return   node.checked = !!~value.indexOf(node.value ) ;

             }

         }

     });

     if ( typeof   $.fixIEAttr == "function" ){

         $.fixIEAttr(valHooks, $.attrHooks);

     }

     return   $;

});

attr_fix模块,里面只有一个补丁函数。

?

define( "attr_fix" , !!top.getComputedStyle, [ "$node" ], function ($){

     $.fixIEAttr = function (valHooks, attrHooks){

         var   rnospaces = /\S+/g, 

         rattrs = /\s+([\w-]+)(?:=( "[^" ]* "|'[^']*'|[^\s>]+))?/g,

         rquote = /^['" ]/,

         defaults = {

             checked: "defaultChecked" ,

             selected: "defaultSelected"

         }

         $.fixDefault = function (node, name, value){

             var   _default =  defaults[name];

             if (_default){

                 node[ _default ] = value;

             }

         }

         if (!( "classList"   in   $.html)){

             $.fn.addClass = function ( item ){

                 if   ( typeof   item == "string" ) {

                     for   ( var   i = 0, el; el = this [i++]; ) {

                         if   ( el.nodeType === 1 ) {

                             if   ( !el.className ) {

                                 el.className = item;

                             } else   {

                                 var   a = (el.className+ " " +item).match( rnospaces );

                                 a.sort();

                                 for   ( var   j = a.length - 1; j > 0; --j)

                                     if   (a[j] == a[j - 1])

                                         a.splice(j, 1);

                                 el.className = a.join( " " );

                             }

                         }

                     }

                 }

                 return   this ;

             } 

             $.fn.removeClass =  function ( item ) {

                 if   ( (item && typeof   item === "string" ) || item === void 0 ) {

                     var   classNames = ( item || ""   ).match( rnospaces ), cl = classNames.length;

                     for   ( var   i = 0, node; node = this [ i++ ]; ) {

                         if   ( node.nodeType === 1 && node.className ) {

                             if   ( item ) { //rnospaces = /\S+/

                                 var   set = " "   + node.className.match( rnospaces ).join( " " ) + " " ;

                                 for   ( var   c = 0; c < cl; c++ ) {

                                     set = set.replace( " "   + classNames[c] + " " , " " );

                                 }

                                 node.className = set.slice( 1, set.length - 1 );

                             } else   {

                                 node.className = "" ;

                             }

                         }

                     }

                 }

                 return   this ;

             }

         }

 

         attrHooks[ "@ie:get" ] = function ( node, name ){

             var   str = node.outerHTML.replace(node.innerHTML, "" ), obj = {}, k, v;

             while   (k = rattrs.exec(str)) { //属性值只有双引号与无引号的情况

                 v = k[2]

                 obj[ k[1].toLowerCase() ] = v ? rquote.test( v ) ? v.slice(1, -1) : v : ""

             }

             return   obj[ name ];

         }

         attrHooks[ "@ie:set" ] = function ( node, name, value ){ 

             var   attr = node.getAttributeNode( name );

             if   ( !attr ) { //不存在就创建一个同名的特性节点

                 attr = node.ownerDocument.createAttribute( name );

                 node.setAttributeNode( attr );

             }

             attr.value = value + ""   ;

         }

          

         var   support = $.support

         if   ( !support.attrInnateValue ) {

             // http://gabriel.nagmay.com/2008/11/javascript-href-bug-in-ie/

             //在IE6-8如果一个A标签,它里面包含@字符,并且没任何元素节点,那么它里面的文本会变成链接值

             $.propHooks[ "href:set"   ] =  attrHooks[ "href:set"   ] = function ( node, name, value ) {

                 var   b

                 if (node.tagName == "A"   && node.innerText.indexOf( "@" ) > 0

                     && !node.children.length){

                     b = node.ownerDocument.createElement( 'b' );

                     b.style.display = 'none' ;

                     node.appendChild(b);

                 }

                 node.setAttribute(name, value+ "" );

                 if   (b) {

                     node.removeChild(b);

                 }

             }

         }

         //========================attrHooks 的相关修正==========================

         if   ( !support.attrInnateHref ) {

             // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx

             //IE的getAttribute支持第二个参数,可以为 0,1,2,4

             //0 是默认;1 区分属性的大小写;2取出源代码中的原字符串值(注,IE67对动态创建的节点没效),4用于取得完整路径

             //IE 在取 href 的时候默认拿出来的是绝对路径,加参数2得到我们所需要的相对路径。

             "href,src,width,height,colSpan,rowSpan" .replace( $.rword, function ( method ) {

                 attrHooks[ method.toLowerCase() + ":get"   ] =  function ( node,name ) {

                     var   ret = node.getAttribute( name, 2 );

                     return   ret == null   ? void 0 : ret;

                 }

             });

             "width,height" .replace( $.rword, function ( attr ){

                 attrHooks[attr+ ":set" ] = function (node, name, value){

                     node.setAttribute( attr, value === ""   ? "auto"   : value+ "" );

                 }

             });

             $.propHooks[ "href:get" ] = function ( node, name ) {

                 return   node.getAttribute( name, 4 );

             };

         }

         if (!document.createElement( "form" ).enctype){ //如果不支持enctype, 我们需要用encoding来映射

             $.propMap.enctype = "encoding" ;

         }

         if   ( !support.attrInnateStyle ) {

             //IE67是没有style特性(特性的值的类型为文本),只有el.style(CSSStyleDeclaration)(bug)

             attrHooks[ "style:get"   ] = function ( node ) {

                 return   node.style.cssText.toLowerCase() || undefined ;

             }

             attrHooks[ "style:set"   ] = function ( node, name, value ) {

                 node.style.cssText = value + "" ;

             }

         }

         //========================valHooks 的相关修正==========================

         if (!support.attrInnateName){ //IE6-7 button.value错误指向innerText

             valHooks[ "button:get" ] =  attrHooks[ "@ie:get" ]

             valHooks[ "button:set" ] =  attrHooks[ "@ie:set" ]

         }

         delete   $.fixIEAttr;

     }

     return   $;

})

到目前为止,lang, css, event, attr模块都分割完成。它比jQuery分割为jquery1.9与jquery-compat-1.9更细腻,带来的性能提升更好。

顺便庆祝一下,此篇是mass Framework专题博文的第100篇博文!

 

 

标签:  javascript ,  mass

 

http://www.cnblogs.com/rubylouvre/archive/2012/12/24/2830798.html

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于mass Framework attr模块 v3的详细内容...

  阅读:38次