classList就不做介绍了,HTML 5的一个操作类名的API。
四个方法:
- add(str)
- remove(str)
- contains(str)
- toggle(str)
需要注意的是,作为参数传入的必须是一个className,而不能是两个或者多个。并且对传入的字符串大小写敏感。在做remove操作时,同名的className也会删除。浏览器支持情况
这里自己封装removeClass和toggleClass的函数,contains就不说了。add和remove类似,也不说了。这里remove用到一个惰性载入,如果浏览器支持,指定相应的函数,无需在下一次执行还需要判断。只是在第一次调用时会损失一点点性能。
function removeClass(element,str){
if (typeof element.classList !== 'undefined'){
removeClass = function (){
element.classList.remove(str);
};
} else {
removeClass = function (){
var classNames = element.className.split(/\s+/),
i = classNames.length;
for (; i--; ){
if(classNames[i] === str){
classNames.splice(i,1);
/*
element.className = classNames.join(" ");
return; //在此直接return真的合适吗?如果有同名的两个className呢?
//这个classList API 可是会将同名的也删除呢。尽管这可能是其他方面的问题。
//比如HTML里不小心放了两个同名className,或者JS中意外添加了同名className,
//不过,因此,也要考虑这方面情况。
*/
}
}
element.className = classNames.join(" ");
};
}
return removeClass(element,str);
}
下面是toggleClass函数,这里用到另一种形式的惰性载入,直接运行一个匿名函数,在函数声明时就指定相应的函数。这种方式,只在代码首次加载时会损失一点性能。
var toggleClass = (function (){
if (typeof document.body.classList !== 'undefined'){
return function (element,str){
element.classList.toggle(str);
};
} else {
return function (element,str){
var classNames = element.className.split(/\s+/),
i = (len = classNames.length);
for (; i--; ){
if(classNames[i] === str){
classNames.splice(i,1);
}
}
if(len === classNames.length){
classNames.push(str);
}
element.className = classNames.join(" ");
};
}
})();
有没发现,上面部分的代码,很大部分是一样。
这个API作为一个模块,支持了remove,也就支持了toggle
对于不支持classList的浏览器,分析下toggle的逻辑:如果原来的没有匹配的className,添加;如果有匹配,删除。而remove的逻辑呢:没有匹配到,什么都不做;有匹配到,删除。其都要循环遍历所有的className组成的数组。
那么,我给removeClass内部,如果匹配到传入的className,做了删除操作后,返回一个true代表已经做了删除操作怎么样。这样,就“告诉”了函数,不要做add操作了。而如果其没有匹配到传入的className,原来是什么都不做,可以给它返回一个false或者 还是什么都不做(默认返回undefined)。既然没有做remove操作,那么就是做add了。
下面请看代码:
var removeClass = null,
toggleClass = null;
if(typeof document.body.classList !== 'undefined'){
removeClass = function (element,str){
element.classList.remove(str);
};
toggleClass = function (element,str){
element.classList.toggle(str);
}
} else {
removeClass = function (elements,str){
var classNames = element.className.split(/\s+/),
i = (len = classNames.length);
for (; i--; ){
if(classNames[i] === str){
classNames.splice(i,1);
}
}
if(len !== classNames.length){
element.className = classNames.join(" ");
return true;
}
};
toggleClass = function (elements,str){
if(!removeClass(element,str)){
element.className += (' ' + str);
}
};
}
当然,性能降低了一点点,涉及到了字符串拼接。不过倒是代码简短了。此外,用的是递减的迭代,所以,也有一点点好处,毕竟添加操作,都是在末尾进行的,无论是前面用的push然后concat,还是这里的字符串拼接。也就是说,其实有的时候for循环只需要执行一步就够了。
此外,可以再扩展下,实现多个className的删除和切换。
function toggleClass2(element,str){
var classNames = element.className.split(/\s+/),
arr = str.split(/\s+/),
i,
j,
len = classNames.length;
for(j = arr.length; j-- ; ){
for (i = classNames.length; i-- ; ){
if(classNames[i] === arr[j]){
classNames.splice(i,1);
}
}
}
if(len === classNames.length){
for(j = arr.length; j-- ; ){
classNames.push(arr[j]);
}
}
element.className = classNames.join(" ");
}
其实也没必要了,原本一个元素的className也就那几个。