/** * @ignore * dom-class * @author lifesinger@gmail.com, yiminghe@gmail.com */ KISSY.add('dom/base/class', function (S, DOM, undefined) { var SPACE = ' ', NodeType = DOM.NodeType, RE_SPLIT = /[\.\s]\s*\.?/, RE_CLASS = /[\n\t]/g; function norm(elemClass) { return (SPACE + elemClass + SPACE).replace(RE_CLASS, SPACE); } S.mix(DOM, /** * @override KISSY.DOM * @class * @singleton */ { /** * Determine whether any of the matched elements are assigned the given classes. * @param {HTMLElement|String|HTMLElement[]} selector matched elements * @param {String} className One or more class names to search for. * multiple class names is separated by space * @return {Boolean} */ hasClass: function (selector, className) { return batch(selector, className, function (elem, classNames, cl) { var elemClass = elem.className, className, j, ret; if (elemClass) { className = norm(elemClass); j = 0; ret = true; for (; j < cl; j++) { if (className.indexOf(SPACE + classNames[j] + SPACE) < 0) { ret = false; break; } } if (ret) { return true; } } }, true); }, /** * Adds the specified class(es) to each of the set of matched elements. * @param {HTMLElement|String|HTMLElement[]} selector matched elements * @param {String} className One or more class names to be added to the class attribute of each matched element. * multiple class names is separated by space */ addClass: function (selector, className) { batch(selector, className, function (elem, classNames, cl) { var elemClass = elem.className, normClassName, setClass, j; if (!elemClass) { elem.className = className; } else { normClassName = norm(elemClass); setClass = elemClass; j = 0; for (; j < cl; j++) { if (normClassName.indexOf(SPACE + classNames[j] + SPACE) < 0) { setClass += SPACE + classNames[j]; } } elem.className = S.trim(setClass); } }, undefined); }, /** * Remove a single class, multiple classes, or all classes from each element in the set of matched elements. * @param {HTMLElement|String|HTMLElement[]} selector matched elements * @param {String} className One or more class names to be removed from the class attribute of each matched element. * multiple class names is separated by space */ removeClass: function (selector, className) { batch(selector, className, function (elem, classNames, cl) { var elemClass = elem.className, className, j, needle; if (elemClass) { if (!cl) { elem.className = ''; } else { className = norm(elemClass); j = 0; for (; j < cl; j++) { needle = SPACE + classNames[j] + SPACE; // 一个 cls 有可能多次出现:'link link2 link link3 link' while (className.indexOf(needle) >= 0) { className = className.replace(needle, SPACE); } } elem.className = S.trim(className); } } }, undefined); }, /** * Replace a class with another class for matched elements. * If no oldClassName is present, the newClassName is simply added. * @param {HTMLElement|String|HTMLElement[]} selector matched elements * @param {String} oldClassName One or more class names to be removed from the class attribute of each matched element. * multiple class names is separated by space * @param {String} newClassName One or more class names to be added to the class attribute of each matched element. * multiple class names is separated by space */ replaceClass: function (selector, oldClassName, newClassName) { DOM.removeClass(selector, oldClassName); DOM.addClass(selector, newClassName); }, /** * Add or remove one or more classes from each element in the set of * matched elements, depending on either the class's presence or the * value of the switch argument. * @param {HTMLElement|String|HTMLElement[]} selector matched elements * @param {String} className One or more class names to be added to the class attribute of each matched element. * multiple class names is separated by space * @param [state] {Boolean} optional boolean to indicate whether class * should be added or removed regardless of current state. */ toggleClass: function (selector, className, state) { var isBool = S.isBoolean(state), has, j; batch(selector, className, function (elem, classNames, cl) { for (j=0; j < cl; j++) { className = classNames[j]; has = isBool ? !state : DOM.hasClass(elem, className); DOM[has ? 'removeClass' : 'addClass'](elem, className); } }, undefined); } }); function batch(selector, value, fn, resultIsBool) { if (!(value = S.trim(value))) { return resultIsBool ? false : undefined; } var elems = DOM.query(selector), len = elems.length, tmp = value.split(RE_SPLIT), elem, ret, classNames = [], t, i; for (i = 0; i < tmp.length; i++) { t = S.trim(tmp[i]); if (t) { classNames.push(t); } } for (i = 0; i < len; i++) { elem = elems[i]; if (elem.nodeType == NodeType.ELEMENT_NODE) { ret = fn(elem, classNames, classNames.length); if (ret !== undefined) { return ret; } } } if (resultIsBool) { return false; } return undefined; } return DOM; }, { requires: ['./api'] }); /* NOTES: - hasClass/addClass/removeClass 的逻辑和 jQuery 保持一致 - toggleClass 不支持 value 为 undefined 的情形(jQuery 支持) */