首页 > 代码库 > 对avalon的类名操作进行升级

对avalon的类名操作进行升级

在对SVG元素进行类名操作时,发现有一个坑爹的事情,它的className竟然是一个对象,因此报一系列BUG。第一次想到的方法是添加setClasses, getClasses两个更底层的方法。于是相应代码变成:

    var rclass = /\s+/g    function getClasses(node) {        if (node && node.className) {            var classes = node.className//SVG元素是返回一个SVGAnimatedString对象            if ("baseVal" in classes) {                classes = classes.baseVal            }            return classes.split(rclass)        }        return []    }    function setClasses(node, cls) {        if (node && node.nodeType === 1) {            if ("baseVal" in node.className) {                node.setAttribute("class", cls)            } else {                node.className = cls            }        }    }    avalon.fn.mix({        hasClass: function(cls) {            var classList = getClasses(this[0])            if (classList.length) {                return (" " + classList.join(" ") + " ").indexOf(" " + cls + " ") > -1            }            return false        },        addClass: function(cls) {            var node = this[0]            if (cls && node && node.nodeType === 1) {                var arr = getClasses(node)                cls.replace(/\S+/g, function(c) {                    if (arr.indexOf(c) === -1) {                        arr.push(c)                    }                })                setClasses(node, arr.join(" "))            }            return this        },        removeClass: function(cls) {            var node = this[0], classList = getClasses(node)            if (cls && classList.length) {                var set = " " + classList.join(" ") + " "                cls.replace(/\S+/g, function(c) {                    set = set.replace(" " + c + " ", " ")                })                setClasses(node, set.slice(1, set.length - 1))            }            return this        },        toggleClass: function(value, stateVal) {            var state = stateVal,                    className, i = 0            var classNames = value.split(rclass)            var isBool = typeof stateVal === "boolean"            while ((className = classNames[i++])) {                state = isBool ? state : !this.hasClass(className)                this[state ? "addClass" : "removeClass"](className)            }            return this        }})

这里的麻烦处是,IE6,IE7不能直接通过getAttribute("class")得到className,而SVG如果直接用className又没有用,于是抽取出getClasses方法,赋值也一样。

在高级浏览器,classList在SVG中是没有兼容问题。看avalon.mobile的相关实现是多么简洁:

    "add,remove".replace(rword, function(method) {        avalon.fn[method + "Class"] = function(cls) {            var el = this[0]            //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26            if (cls && typeof cls === "string" && el && el.nodeType == 1) {                cls.replace(/\S+/g, function(c) {                    el.classList[method](c)                })            }            return this        }    })        avalon.fn.mix({        hasClass: function(cls) {            var el = this[0] || {} //IE10+, chrome8+, firefox3.6+, safari5.1+,opera11.5+支持classList,chrome24+,firefox26+支持classList2.0            return el.nodeType === 1 && el.classList.contains(cls)        },        toggleClass: function(value, stateVal) {            var state = stateVal,                    className, i = 0            var classNames = value.split(/\s+/)            var isBool = typeof stateVal === "boolean"            var node = this[0] || {}, classList            if (classList = node.classList) {                while ((className = classNames[i++])) {                    state = isBool ? state : !classList.contains(className)                    classList[state ? "add" : "remove"](className)                }            }            return this        }})

因此新的思路来了,为IE6-9等实现一个classList,通过它来屏蔽底层,再在classList的基础上构建avalon的addClass, removeClass, toggleClass,hasClass

    function ClassList(node) {        if (!("classList" in node)) {            node.classList = {                node: node,                toString: function() {                    var node = this.node                    return (node.hasAttribute ? node.getAttribute("class") || "" : node.className).split(/\s+/).join(" ")                },                contains: function(cls) {                    return (" " + this + " ").indexOf(" " + cls + " ") > -1                },                _set: function(cls) {                    var node = this.node                    if (typeof node.className == "string") {                        node.className = cls                    } else {                        node.setAttribute("class", cls)                    }                },                add: function(cls) {                    if (!this.contains(cls)) {                        this._set(this + " " + cls)                    }                },                remove: function(cls) {                    this._set((" " + this + " ").replace(" " + cls + " ", " ").trim())                }            }        }        return node.classList    }    "add,remove".replace(rword, function(method) {        avalon.fn[method + "Class"] = function(cls) {            var el = this[0]            //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26            if (cls && typeof cls === "string" && el && el.nodeType == 1) {                cls.replace(/\S+/, function(c) {                    ClassList(el)[method](c)                })            }            return this        }    })    avalon.fn.mix({        hasClass: function(cls) {            var el = this[0] || {}            return el.nodeType === 1 && ClassList(el).contains(cls)            return false        },        toggleClass: function(value, stateVal) {            var state = stateVal,                    className, i = 0            var classNames = value.split(/\s+/)            var isBool = typeof stateVal === "boolean"            while ((className = classNames[i++])) {                state = isBool ? state : !this.hasClass(className)                this[state ? "addClass" : "removeClass"](className)            }            return this        }})
http://baike.baidu.com/view/957693.htm