1 /** 2 * @fileOverview dom-class 3 * @author lifesinger@gmail.com,yiminghe@gmail.com 4 */ 5 KISSY.add('dom/class', function (S, DOM, undefined) { 6 7 var SPACE = ' ', 8 REG_SPLIT = /[\.\s]\s*\.?/, 9 REG_CLASS = /[\n\t]/g; 10 11 function norm(elemClass) { 12 return (SPACE + elemClass + SPACE).replace(REG_CLASS, SPACE); 13 } 14 15 S.mix(DOM, 16 17 /** 18 * @lends DOM 19 */ 20 { 21 /** 22 * Determine whether any of the matched elements are assigned the given classes. 23 * @param {HTMLElement|String|HTMLElement[]} [selector] matched elements 24 * @param {String} className One or more class names to search for. 25 * multiple class names is separated by space 26 * @return {Boolean} 27 */ 28 hasClass:function (selector, className) { 29 return batch(selector, className, function (elem, classNames, cl) { 30 var elemClass = elem.className; 31 if (elemClass) { 32 var className = norm(elemClass), 33 j = 0, 34 ret = true; 35 for (; j < cl; j++) { 36 if (className.indexOf(SPACE + classNames[j] + SPACE) < 0) { 37 ret = false; 38 break; 39 } 40 } 41 if (ret) { 42 return true; 43 } 44 } 45 }, true); 46 }, 47 48 /** 49 * Adds the specified class(es) to each of the set of matched elements. 50 * @param {HTMLElement|String|HTMLElement[]} [selector] matched elements 51 * @param {String} className One or more class names to be added to the class attribute of each matched element. 52 * multiple class names is separated by space 53 */ 54 addClass:function (selector, className) { 55 batch(selector, className, function (elem, classNames, cl) { 56 var elemClass = elem.className; 57 if (!elemClass) { 58 elem.className = className; 59 } else { 60 var normClassName = norm(elemClass), 61 setClass = elemClass, 62 j = 0; 63 for (; j < cl; j++) { 64 if (normClassName.indexOf(SPACE + classNames[j] + SPACE) < 0) { 65 setClass += SPACE + classNames[j]; 66 } 67 } 68 elem.className = S.trim(setClass); 69 } 70 }, undefined); 71 }, 72 73 /** 74 * Remove a single class, multiple classes, or all classes from each element in the set of matched elements. 75 * @param {HTMLElement|String|HTMLElement[]} [selector] matched elements 76 * @param {String} className One or more class names to be removed from the class attribute of each matched element. 77 * multiple class names is separated by space 78 */ 79 removeClass:function (selector, className) { 80 batch(selector, className, function (elem, classNames, cl) { 81 var elemClass = elem.className; 82 if (elemClass) { 83 if (!cl) { 84 elem.className = ''; 85 } else { 86 var className = norm(elemClass), 87 j = 0, 88 needle; 89 for (; j < cl; j++) { 90 needle = SPACE + classNames[j] + SPACE; 91 // 一个 cls 有可能多次出现:'link link2 link link3 link' 92 while (className.indexOf(needle) >= 0) { 93 className = className.replace(needle, SPACE); 94 } 95 } 96 elem.className = S.trim(className); 97 } 98 } 99 }, undefined); 100 }, 101 102 /** 103 * Replace a class with another class for matched elements. 104 * If no oldClassName is present, the newClassName is simply added. 105 * @param {HTMLElement|String|HTMLElement[]} [selector] matched elements 106 * @param {String} oldClassName One or more class names to be removed from the class attribute of each matched element. 107 * multiple class names is separated by space 108 * @param {String} newClassName One or more class names to be added to the class attribute of each matched element. 109 * multiple class names is separated by space 110 */ 111 replaceClass:function (selector, oldClassName, newClassName) { 112 DOM.removeClass(selector, oldClassName); 113 DOM.addClass(selector, newClassName); 114 }, 115 116 /** 117 * Add or remove one or more classes from each element in the set of 118 * matched elements, depending on either the class's presence or the 119 * value of the switch argument. 120 * @param {HTMLElement|String|HTMLElement[]} [selector] matched elements 121 * @param {String} className One or more class names to be added to the class attribute of each matched element. 122 * multiple class names is separated by space 123 * @param [state] {Boolean} optional boolean to indicate whether class 124 * should be added or removed regardless of current state. 125 */ 126 toggleClass:function (selector, className, state) { 127 var isBool = S.isBoolean(state), has; 128 129 batch(selector, className, function (elem, classNames, cl) { 130 var j = 0, className; 131 for (; j < cl; j++) { 132 className = classNames[j]; 133 has = isBool ? !state : DOM.hasClass(elem, className); 134 DOM[has ? 'removeClass' : 'addClass'](elem, className); 135 } 136 }, undefined); 137 } 138 }); 139 140 function batch(selector, value, fn, resultIsBool) { 141 if (!(value = S.trim(value))) { 142 return resultIsBool ? false : undefined; 143 } 144 145 var elems = DOM.query(selector), 146 len = elems.length, 147 tmp = value.split(REG_SPLIT), 148 elem, 149 ret; 150 151 var classNames = []; 152 for (var i = 0; i < tmp.length; i++) { 153 var t = S.trim(tmp[i]); 154 if (t) { 155 classNames.push(t); 156 } 157 } 158 for (i = 0; i < len; i++) { 159 elem = elems[i]; 160 if (elem.nodeType==DOM.ELEMENT_NODE) { 161 ret = fn(elem, classNames, classNames.length); 162 if (ret !== undefined) { 163 return ret; 164 } 165 } 166 } 167 168 if (resultIsBool) { 169 return false; 170 } 171 return undefined; 172 } 173 174 return DOM; 175 }, { 176 requires:["dom/base"] 177 }); 178 179 /** 180 * NOTES: 181 * - hasClass/addClass/removeClass 的逻辑和 jQuery 保持一致 182 * - toggleClass 不支持 value 为 undefined 的情形(jQuery 支持) 183 */ 184