• mass Framework attr模块


    属性在这里只是一个统称,它对应两个术语attribute与property。attribute是指用户通过setAttribute设置的自定义属性,其值只能是字符串,如果没有显式定义,用getAttribute取值为undefined。property是指元素固有属性,像title, className,tabIndex, src, checked等等。这些属性的值可以是五花八门,如className总是字符串,tabIndex总是数字(它只限表单元素与个别能获取焦点的元素),表单元素的form属性总是指向其外围的表单对象(当然如果它没有放在form标签之内是不存在的), checked, disabled , defer等属性,如果此元素支持它,总总是返回布尔值。这些固有属性,如果没有显示定义,基本上是返回null(如果className, value则是字符串)。

            document.body.className = function(){}
            alert(typeof document.body.className )//无法重写,返回"string"
    

    如何处理这些庞杂的属性就是本模块要做的事情。它拥有如下四类方法。

    val用于设置获取表单元素的value值,如果是普通元素则内部转交prop方法去处理。

    hasClass, addClass, removeClass, toggleClass, replaceClass用于判定,添加, 移除, 切换与替换元素节点的className属性。className是一个特别的属性,它对应由多个类名组成的字符串,因此从prop独立出来区别对待。

    attr,removeAttr用于处理元素节点的自定义属性,如果发现对象是window,则转交prop方法处理。

    prop,removeProp用于固有属性。在下面给出的JS代码中,给出两份清单,一份是用于属性名映射,如著名的class变成className,for变成htmlFor,rowspan变成rowSpan....另一个是布尔属性,如async,autofocus,checked....它们比jquery源码那份更齐全了。

    val, attr, prop 都支配着复数个适配器,确保能取得正确的值。

    ;
    (function(global,DOC){
        var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')];
        dom.define("attr","node", function(){
            var rclass = /(^|\s)(\S+)(?=\s(?:\S+\s)*\2(?:\s|$))/g,
            rfocusable = /^(?:button|input|object|select|textarea)$/i,
            rclickable = /^a(?:rea)?$/i,
            rspaces = /\s+/,
            support = dom.support,
            nodeAdapter,
            valOne = {
                "SELECT":"select",
                "OPTION":"option",
                "BUTTON":"button"
            },
            getValType = function(node){
                return "form" in node && (valOne[node.tagName] || node.type)
            }
            //Breast Expansion - Kate beim Arzt
            dom.implement({
                /**
                 *  为所有匹配的元素节点添加className,添加多个className要用空白隔开
                 *  如dom("body").addClass("aaa");dom("body").addClass("aaa bbb");
                 *  <a href="http://www.cnblogs.com/rubylouvre/archive/2011/01/27/1946397.html">相关链接</a>
                 */
                addClass:function(value){
                    if ( typeof value == "string") {
                        for ( var i = 0, el; el = this[i++]; ) {
                            if ( el.nodeType === 1 ) {
                                if ( !el.className ) {
                                    el.className = value;
                                } else {
                                    el.className = (el.className +" "+value).replace(rclass,"")
                                }
                            }
                        }
                    }
                    return this;
                },
                //如果第二个参数为true,则只判定第一个是否存在此类名,否则对所有元素进行操作;
                hasClass: function( value, every ) {
                    var method = every === true ? "every" : "some"
                    var rclass = new RegExp('(\\s|^)'+value+'(\\s|$)');//判定多个元素,正则比indexOf快点
                    return dom.slice(this)[method](function(el){
                        return "classList" in el ? el.classList.contains(value):
                        (el.className || "").match(rclass);
                    });
                },
                //如果不传入类名,则去掉所有类名,允许传入多个类名
                removeClass: function( value ) {
                    if ( (value && typeof value === "string") || value === undefined ) {
                        var classNames = (value || "").split( rspaces );
                        for ( var i = 0, node; node = this[i++]; ) {
                            if ( node.nodeType === 1 && node.className ) {
                                if ( value ) {
                                    var className = (" " + node.className + " ").replace(rspaces, " ");
                                    for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
                                        className = className.replace(" " + classNames[c] + " ", " ");
                                    }
                                    node.className = className.trim();
                                } else {
                                    node.className = "";
                                }
                            }
                        }
                    }
                    return this;
                },
                //如果存在(不存在)就删除(添加)一个类。对所有匹配元素进行操作。
                toggleClass:function(value){
                    var classNames = value.split(rspaces ), i, className;
                    var type = typeof value
                    return this.each(function(el) {
                        i = 0;
                        if(el.nodeType === 1){
                            var self = dom(el);
                            if(type == "string" ){
                                while ( (className = classNames[ i++ ]) ) {
                                    self[ self.hasClass( className ) ? "removeClass" : "addClass" ]( className );
                                }
                            } else if ( type === "undefined" || type === "boolean" ) {
                                if ( el.className ) {
                                    self._data( "__className__", el.className );
                                }
                                el.className = el.className || value === false ? "" : self.data( "__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.split(rspaces), arr2 = [];
                            for (var j = 0; j<arr.length; j++) {
                                arr2.push(arr[j] != old ? arr[j] : neo);
                            }
                            node.className = arr2.join(" ");
                        }
                    }
                    return this;
                },
                val:  function( value ) {
                    var  node = this[0], adapter = dom.valAdapter;
                    if ( !arguments.length ) {//读操作
                        if ( node && node.nodeType == 1 ) {
                            //处理select-multiple, select-one,option,button
                            var ret =  (adapter[ getValType(node)+":get" ] || dom.propAdapter[ "_default:get" ])(node, "value");
                            return  ret || ""
                        }
                        return undefined;
                    }
                    //强制将null/undefined转换为"", number变为字符串
    
                    if(Array.isArray(value)){
                        value = value.map(function (value) {
                            return value == null ? "" : value + "";
                        });
                    }else if(isFinite(value)){
                        value += "";
                    }else{
                        value = value || "";//强制转换为数组
                    }
                    return this.each(function(node) {//写操作
                        if ( node.nodeType == 1 ) {
                            (adapter[ getValType(node)+":set" ] || dom.propAdapter[ "_default:set" ])(node, "value",value);
                        }
                    });
                },
                removeAttr: function( name ) {
                    name = dom.attrMap[ name ] || name;
                    var isBool = boolOne[name];
                    return this.each(function(node) {
                        if(node.nodeType === 1){
                            dom["@remove_attr"]( node, name, isBool );
                        }
                    });
                },
                removeProp: function( name ) {
                    name = dom.propMap[ name ] || name;
                    return this.each(function() {
                        // try/catch handles cases where IE balks (such as removing a property on window)
                        try {
                            this[ name ] = undefined;
                            delete this[ name ];
                        } catch( e ) {}
                    });
                }
            });
            dom.extend({
                attrMap:{
                    tabindex: "tabIndex"
                },
                propMap:{
                    "accept-charset": "acceptCharset",
                    "char": "ch",
                    charoff: "chOff",
                    "class": "className",
                    "for": "htmlFor",
                    "http-equiv": "httpEquiv"
                },
                //内部函数,原则上拒绝用户的调用
                "@remove_attr": function( node, name, isBool ) {
                    var propName;
                    name = dom.attrMap[ name ] || name;
                    //如果支持removeAttribute,则使用removeAttribute
                    dom.attr( node, name, "" );
                    node.removeAttribute( name );
                    // 确保bool属性的值为bool
                    if ( isBool && (propName = dom.propMap[ name ] || name) in node ) {
                        node[ propName ] = false;
                    }
                },
                attrAdapter: {//最基本的适配器
                    "_default:get":function(node,name){
                        var ret =  node.getAttribute( name ) ;
                        return ret === null ? undefined : ret;
                    },
                    "_default:set":function(node, name, value){
                        //注意,属性名不能为数字,在FF下会报错String contains an invalid character" code: "5
                        node.setAttribute( name, "" + value )
                    },
                    "tabIndex:get":function( node ) {
                        // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
                        var attributeNode = node.getAttributeNode( "tabIndex" );
                        return attributeNode && attributeNode.specified ?
                        parseInt( attributeNode.value, 10 )  : 
                        rfocusable.test(node.nodeName) || rclickable.test(node.nodeName) && node.href  ? 0 : undefined;
                    }
    
                },
                propAdapter:{
                    "_default:get":function(node,name){
                        return node[ name ]
                    },
                    "_default:set":function(node, name, value){
                        node[ name ] = value;
                    },
                    "value:set":function(node, name, value){
                        return dom.fn.val.call([node],value) ;
                    },
                    "value:get":function(node){
                        return dom.fn.val.call([node])
                    }
                },
                valAdapter:  {
                    "option:get":  function( elem ) {
                        var val = elem.attributes.value;
                        return !val || val.specified ? elem.value : elem.text;
                    },
                    "select:get": function( node ) {
                        var index = node.selectedIndex, values = [],options = node.options, value;
                        var one = !node.multiple
                        if ( index < 0 ) {
                            return null;
                        }
                        for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
                            var option = options[ i ];
                            if ( option.selected  ) {
                                value = option.value || option.text;
                                if ( one ) {
                                    return value;
                                }
                                values.push( value );
                            }
                        }
                        if(one && !value.length && node.length){
                            return dom(options[index]).val();
                        }
                        return values;
                    },
                    "select:set": function( node, name,values ) {
                        dom.lang(node.options).forEach(function(el){
                            el.selected = values.indexOf(el.value || el.text) >= 0;
                        });
                        if ( !values.length ) {
                            node.selectedIndex = -1;
                        }
                        return values;
                    }
                }
            });
    
            //attr方法只能用于元素节点,只能写入字符串,只能取得字符串或undefined
            //prop方法只能用于原生的事件发送器,能写入除flase,undefined之外的各种数据,如果是bool属性必返回bool值
            //attr是用于模拟set/getAttribute操作,用于设置或取得对象的自定义属性
            //prop是用于模拟obj[name]操作,用于设置或取得对象的固有属性
            "attr,prop".replace(dom.rword,function(method){
                dom[method] = function( node, name, value ) {
                    if(node  && (dom["@emitter"] in node || method == "prop")){
                        if ( !("getAttribute" in node) ) {
                            method = "prop";
                        }
                        var notxml = !node.nodeType || !dom.isXML(node);
                        //对于HTML元素节点,我们需要对一些属性名进行映射
                        name = notxml && dom[method+"Map"][ name ] || name;
    
                        var adapter = dom[method+"Adapter"];
    
                        if(method === "attr" && node.tagName){
                            //比如input的readonly的取值可以为true false readonly
                            if(boolOne[name] && (typeof value === "boolean" || value === undefined || value.toLowerCase() === name.toLowerCase())) {
                                name = dom.propMap[name] || name;
                                adapter = boolAdapter;
                            }else if(nodeAdapter ){
                                adapter = nodeAdapter;
                            }
                        }
                        
                        if ( value !== void 0 ){
                            if( method === "attr" && value === null){  //移除属性
                                return  dom["@remove_"+method]( node, name );
                            }else{ //设置属性
                                return (notxml && adapter[name+":set"] || adapter["_default:set"])( node, name, value );
                            }
                        }  //获取属性
                        return (notxml && adapter[name+":get"] || adapter["_default:get"])( node, name );
                    }
                };
    
                dom.fn[method] = function( name, value ) {
                    return dom.access( this, name, value, dom[method], dom[method]);
                }
            });
    
    
            //========================propAdapter 的相关补充==========================
            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(dom.rword, function(name){
                dom.propMap[name.toLowerCase()] = name;
            });
            //safari IE9 IE8 我们必须访问上一级元素时,才能获取这个值
            if ( !support.attrSelected ) {
                dom.propAdapter[ "selected:get" ] = function( parent ) {
                    var node = parent
                    for(var i= -3;i++; parent.selectedIndex, parent = parent.parentNode){};
                    return node.selected;
                }
            }
            //========================attrAdapter 的相关补充==========================
            if ( !support.attrHref ) {
                //IE的getAttribute支持第二个参数,可以为 0,1,2,4
                //0 是默认;1 区分属性的大小写;2取出源代码中的原字符串值(注,IE67对动态创建的节点没效)。
                //IE 在取 href 的时候默认拿出来的是绝对路径,加参数2得到我们所需要的相对路径。
                "href,src,width,height,colSpan,rowSpan".replace(dom.rword,function(method ) {
                    dom.attrAdapter[ method + ":get" ] =  function( node,name ) {
                        var ret = node.getAttribute( name, 2);
                        return ret === null ? undefined : ret;
                    }
                });
            }
            //IE67是没有style特性(特性的值的类型为文本),只有el.style(CSSStyleDeclaration)(bug)
            if ( !support.attrStyle ) {
                dom.attrAdapter[ "style:get" ] = function( node ) {
                    return node.style.cssText.toLowerCase();
                }
                dom.attrAdapter[ "style:set" ] = function( node, value ) {
                    return (node.style.cssText = "" + value);
                }
            }
    
            //=========================valAdapter 的相关补充==========================
            //checkbox的value默认为on,唯有Chrome 返回空字符串
            if ( !support.checkOn ) {
                "radio,checkbox".replace(dom.rword,function(name) {
                    dom.valAdapter[ name + ":get" ] = function( node ) {
                        return node.getAttribute("value") === null ? "on" : node.value;
                    }
                });
            }
            //处理单选框,复选框在设值后checked的值
            "radio,checkbox".replace(dom.rword,function(name) {
                dom.valAdapter[ name + ":set" ] = function( node, name, value) {
                    if ( Array.isArray( value ) ) {
                        return node.checked = !!~value.indexOf(node.value ) ;
                    }
                }
            });
            //=========================nodeAdapter 的相关补充=========================
            //如果我们不能通过el.getAttribute("class")取得className,必须使用el.getAttribute("className")
            if(!support.attrProp){
                dom.attrMap = dom.propMap;
                var fixSpecified = dom.oneObject("name,id")
                //注意formElement[name] 相等于formElement.elements[name],会返回其辖下的表单元素
                nodeAdapter = dom.mix({
                    "_default:get": function( node, name ) {
                        var ret = node.getAttributeNode( name );
                        return ret && (fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified) ?  ret.nodeValue : undefined;
                    },
                    "_default:set": function( node, name, value ) {
                        var ret = node.getAttributeNode( name );
                        if ( !ret ) {
                            ret = node.ownerDocument.createAttribute( name );
                            node.setAttributeNode( ret );
                        }
                        return (ret.nodeValue = value + "");
                    }
                },dom.attrAdapter,false);
                "width,height".replace(dom.rword,function(attr){
                    nodeAdapter[attr+":set"] = function(node, name, value){
                        node.setAttribute( attr, value === "" ? "auto" : value+"");
                    }
                });            
                dom.valAdapter["button:get"] = nodeAdapter["_default:get"];
                dom.valAdapter["button:set"] = nodeAdapter["_default:set"];
            }
            //=========================boolAdapter 的相关补充=========================
            var boolAdapter = dom.mix({},dom.attrAdapter,false);
            var bools =  "async,autofocus,checked,declare,disabled,defer,defaultChecked,contentEditable,ismap,loop,multiple,noshade,noresize,readOnly,selected"
            var boolOne = dom.oneObject( support.attrProp ? bools.toLowerCase() : bools );
            bools.replace(dom.rword,function(method) {
                //bool属性在attr方法中只会返回与属性同名的值或undefined
                boolAdapter[method+":get"] = function(node,name){
                    var attrNode;
                    return dom.prop( node, name ) === true || ( attrNode = node.getAttributeNode( name ) ) && attrNode.nodeValue !== false ?
                    name.toLowerCase() :
                    undefined;
                }
                boolAdapter[method+":set"] = function(node,name,value){
                    if ( value === false ) {//如果设置为false,直接移除,如设置input的readOnly为false时,相当于没有只读限制
                        dom["@remove_attr"]( node, name, true );
                    } else {
                        if ( name in node ) {
                            node[ name ] = true;
                        }
                        node.setAttribute( name, name.toLowerCase() );
                    }
                    return name;
                }
            });
    
        });
    })(this, this.document);
    /*
    2011.8.2
    将prop从attr分离出来
    添加replaceClass方法
    2011.8.5
    重构val方法
    2011.8.12
    重构replaceClass方法
    2011.10.11
    重构attr prop方法
     **/
    
  • 相关阅读:
    java正则表达式验证
    sql之left join、right join、inner join的区别
    Servlet 输出图片验证码
    Myeclipse不能使用alt+/快捷方式的解决方法
    两个递增数据组合成一个递增数据(不适用)
    java回调机制
    java中的继承问题
    oracle自定义函数:将字符串中的特殊字符转化为逗号
    oracle常用的函数
    oracle中导入dmp数据注意事项
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2138744.html
Copyright © 2020-2023  润新知