在对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