zoukankan      html  css  js  c++  java
  • 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方法
     **/
    
  • 相关阅读:
    1063. Set Similarity
    A1047. Student List for Course
    A1039. Course List for Student
    最大公约数、素数、分数运算、超长整数计算总结
    A1024. Palindromic Number
    A1023. Have Fun with Numbers
    A1059. Prime Factors
    A1096. Consecutive Factors
    A1078. Hashing
    A1015. Reversible Primes
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2138744.html
Copyright © 2011-2022 走看看