这篇文章发布于 2013年07月15日,星期一,17:06,归类于 JS相关。 阅读 186644 次, 今日 3 次 20 条评论
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=3483
一、其实事情的发展就像切水果
如果我们把元素的类名操作比作“切水果”游戏的话,其中一个单独的类名就好比“水果”或“炸弹”!
DOM Level 2时代,类名的获取与设置,多半使用className
属性,className
的生效近似切水果的“一刀切”。在web的初期,交互什么的其实很简单的来;就像切水果刚开始的时候,一次就一个水果飞上来,一刀“咔嚓”切了就好,就像使用className
赋个类名值,就算偶尔冒出2个水果,className
也可以一刀切搞定的。
但是,随着web的发展,交互的逐渐复杂,一次出现的水果越来越多,已经不是className
一刀就可以搞定的了;而且,还有炸弹,className
一刀切可能就自寻死路了。
为了应对这种需求,显然HTML5要做点什么,于是classList
API出现了,可以有针对性地切水果,不会碰到炸弹什么的……
这就是事情从className
发展到classList
的缘由与过程。
classList
虽改进,但是,从效果上讲,还是敌不过jQuery等外挂,后面会提到。
二、浏览器们,你们怎么看?
classList
实际上已经出现好多年了,因此,自然,FireFox浏览器,Chrome浏览器都支持的很。IE家族中,从IE10浏览器开始才开始认可classList
能进能退的非一刀切做法。
手机上,Android 3.0+以上才开始支持,哦,该死,这很糟糕。现在手机web app要求android 2.*也要支持。
截止今日,兼容性见下截图:
实时兼容性查看点击这里。
由此可见,在PC上,如果要判断是否是IE10+以及其他现代浏览器,可以试试:document.body.classList
是否为undefined
.
三、classList API概要
假设有这么个空白页面:
<body class="a b c"></body>
在Firebug以及Chrome控制台中,运行:
document.body.classList
返回的分别是:
可见其直接暴露的API有:
- length 属性,表示元素类名的个数,只读
- item() 支持一个参数,为类名的索引,返回对应的类名,例如上例:
document.body.classList.item(0);
结果是:
"a"
.如果索引超出范围,例如:
document.body.classList.item(3);
结果是:
null
. - add() 支持一个类名字符串参数。表示往类名列表中新增一个类名;如果之前类名存在,则添加忽略。例如:
document.body.classList.add("c"); document.body.classList.length // 3
此函数方法执行的返回值是
undefined
, 因此,classList
的add()
方法是无法级联的。下面的remove()
方法也是如此。 - remove() 支持一个类名字符串参数。表示往类名列表中移除该类名。例如:
document.body.classList.remove("c"); document.body.classList.length // 2
有点对应于jQuery中的
removeClass()
方法,然后者返回包装器对象本身,可级联;这里的remove()
方法返回undefined
. - toggle() 支持一个类名字符串参数。无则加勉,有则移除之意。若类名列表中有此类名,移除之,并返回
false
; 如果没有,则添加该类名,并返回true
.更新于当日
部分现代浏览器,例如Chrome浏览器以及Firefox 24的toggle()
方法已经支持第2个参数,.toggle(token, switch)
. 其中参数switch
为Boolean类型值,如果为true
表示添加,如果为false
则表示移除。并返回该Boolean值。 - contains() 支持一个类名字符串参数。表示往类名列表中是否包含该类名。有点对应jQuery中的
hasClass
方法,注意,这里的是contains
而不是contain
,后面有个s
哦!返回值很易懂的。如果包含,则返回
true
, 不包含,则false
. 例如:document.body.classList.contains("c"); // false 因为"c"上面remove掉了
在Firebug控制台结果中,我们还看到了toString()方法。实际上,Chrome浏览器也有(未直接显示),IE10也有,不过,似乎来自层级更高对象的继承。从下面截图中的属性颜色区分可见一斑:
就作用上讲,等同于className
. 例如:
document.body.classList.toString() === document.body.className; // true
四、classList的本质-DOMTokenList
classList
的返回值显示,其本质上是DOMTokenList
– DOM标记列表.
DOMTokenList
这种类型表示一组空间分隔的标记。通常由HTMLElement.classList
,HTMLLinkElement.relList
,HTMLAnchorElement.relList
或HTMLAreaElement.relList
返回。从0开始的类JavaScript数组索引。DOMTokenList
始终是区分大小写的。
在FireFox以及Chrome下,我们执行typeof DOMTokenList
的结果是:"function"
; 但是在IE10下,却是:"object"
.
同时虽然typeof
结果为"function"
,但是执行DOMTokenList()
会报”Illegal constructor”错误;IE10执行DOMTokenList()
也会报错,错误是”缺少函数”。
因此,试图通过typeof obj == "function"
来判断obj
就是个函数的做法是不完全正确的。
五、classList的局限
classList
除了上面提到的不能级联这个无关痛痒的局限外,还有个比较头疼的局限,就是不能一次add
或remove
或toggle
多个类名。//zxx: 级联指的是$().a().b().c()这种可以连在一起调用方法的写法。
例如:
document.body.classList.add("c d"); // Error: String contains an invalid character document.body.classList.add("c\x20d"); // Error: String contains an invalid character document.body.classList.remove("c d"); // Error: String contains an invalid character
我们要想多类名处理,需要一个一个来,例如:
var clList = document.body.classList;
clList.add("d");
clList.add("e");
clList.toString(); // "a b c d e"
这一点来看,原生的classList
API要比jQuery或MooTools等框架的addClass/removeClass/...
等类名相关外挂方法弱爆了。然而,classList
API没有恋爱经历、非常单纯。因此,虽然胸小了一点;但是,富豪就是喜欢之。
六、classList的扩展
add
的参数个数限制等方法好比生孩子,一次只能生一个,这符合国家要求符合规范。但是,有些有钱人,就像张艺谋,就愁孩子少,恨不能一次生他3个,咋办呢?我们可以试试对classList
做扩展,例如扩展一个adds
方法,可以一次添加多个类名,多个类名以空格分隔:
DOMTokenList.prototype.adds = function(tokens) { tokens.split(" ").forEach(function(token) { this.add(token); }.bind(this)); return this; }; // 看看能不能一下子生3个孩子 var clList = document.body.classList; clList.adds("child1 child2 child3").toString(); // "a b c child1 child2 child3"
这样,只要肚子够大,想生几个就可以生几个了,中国足球说不定就有希望了——11胞胎争霸全球!
其他些方法,您可以做类似扩展。
但是,平心而论,单类名一个一个添加虽然苦逼了点,但是,个人情感上,因为原汁原味,更喜欢!
//zxx: ios4似乎并未支持bind
方法,因此,上面的扩展如果在手机上使用,需稍作调整。
补充:classList的使IE9浏览器也兼容的polyfill
参见此项目:https://github.com/eligrey/classList.js/
七、结语
随着JS API的越来越丰富,以及越来越多在现代浏览器、尤其手机上的一些折腾,让我越来越疑惑一些庞大框架的存在意义。
有人可能会提出:原生的API方法名太长了,例如:addEventListener
, getComputedStyle
, insertAdjacentHTML
, getBoundingClientRect
等。
“名字长,不好记啊……”
我:“没错,然后呢……”
“文件大啊……”
我:“保留,然后呢……”
“新人不好上手啊……”
我:“算是,然后呢……”
“……”
我:“没话说了吧,那我来说!”
1. 关于“名字长,不好记”
你父母手机号是?啊,不知道啊!那确实名字太长,不太好记。
2. 关于“文件大啊”
小明工作40年,交的养老保险金有100万,0存款,每个月可以拿4000, 每月结余2000; 大明工作40年,养老保险一分钱没交,存了100万,每月结余-3000。结果,小明笑大明每个月的开销比他多1000块,说你没交养老保险亏大了。
dom.addEventListener
和$(dom).bind
哪个字符多,显然前者,小明你赢了!
那下面代码呢?
var addEventListener = "addEventListener"; dom[addEventListener] var bind = "bind"; $(dom)[bind]
小明,你又赢了!JS压缩之后呢?
var a= "addEventListener"; dom[a] var b= "bind"; $(dom)[b]
小明,你又赢了!那多调用几次呢?
var a= "addEventListener"; dom[a] dom[a] dom[a] dom[a] dom[a] var b= "bind"; $(dom)[b] $(dom)[b] $(dom)[b] $(dom)[b] $(dom)[b]
一样多,小明,你还没输!好吧,我再次认为你是对的。
3. 关于“新人不好上手”
林远图封装了《葵花宝典》,美名曰“辟邪剑法”,比较好上手,结果呢,其儿孙泛泛。倒是林平之,学习了盗版原生的《葵花宝典》,虽然不好上手(要咬抹布挥刀那个),但是,结果秒了余沧海,也算高手之列了。
啊?原来你是招新人来打杂的,sorry, 网上的框架、插件确实比较适合你们。
zepto.js算是蛮简洁的移动框架了,实际上,(纠正:zepto.js
有借鉴jqmobi.js
(之前被intel收购,改名为appframework
)的嫌疑zepto.js
是被jqmobi.js
借鉴),而后者似乎更小,我看了下,gzip后6.7K
, 小于前者9.7k
(因含touch
相关事件重写). 但是,我个人看来,还是太大了,尤其那些DOM相关的API,都通通会娘家去吧,gzip后2~3K
足矣。
天气很热,想钓鱼了!
本文为原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=3483
(本篇完)
- 我是如何理解"Another JavaScript quiz"中的题目 (0.233)
- 小tips: PC端传统网页试试使用Zepto.js进行开发 (0.233)
- HTML全局属性列表大全 (0.233)
- CSS+JavaScript实现页面不同布局的切换 (0.155)
- 基于active,checked等状态类名的web前端交互开发 (0.155)
- jQuery诞生记-原理与机制 (0.134)
- 我对原型对象中this的一个懵懂错误认识 (0.116)
- Proxy是代理,Reflect是干嘛用的? (0.116)
- jQuery与MooTools库的一些比对 (0.030)
- before(),after(),prepend(),append()等新DOM方法简介 (0.030)
- 使用document.scrollingElement控制窗体滚动高度 (RANDOM - 0.013)
林远图并不是封装了《葵花宝典》
哈哈哈 博主好可爱
旭哥,现在是可以添加好几个的,赶紧更新
试了下添加多个类可以酱紫:
document.body.classList.add(“a”,”b”);
打卡2
那个adds方法拓展那里, this访问不到add方法吧 , 鑫鑫
今天也发现了classList这个API.百度了下.资料很少.W3C上也查不到.现在html5里面的JS API貌似没有一个比较齐全的查询途径。
楼主你好,今天偶然看一个视频教程,里面看到了classList这个,以前没有见过,遂百度google之,英文结果居多,在w3cschool里面也没有找到,只看控制台里面返回classLis的API,不是很清楚各个函数的具体用法,看到楼主的博文,收货不少,谢谢分享;同时也有一个问题,博文:五、classList的局限里面提到,add方法是不能一次添加多个类名的,按照楼主的传参方法确实是不行的,但是像这种add(className1,className2,…)是可以的,但是想到这样调用add,obj.classList.add.applay(obj,[className1,className2,..])却提示Illegal invocation ,不知为何?
@damocles 写这篇文章的时候,add方法确实不能一次添加多个。浏览器是不断改进的~
用obj.classList.add.applay(obj.classList,[className1,className2,..])就可以了
这个结语很有意思!!果断练葵花宝典,就是隐隐有点蛋疼~~
哈哈
写的不错,很容易让人明白,还挺幽默的
DOMTokenList.prototype.adds = function(tokens) {
tokens.split(” “).forEach(function(token) {
this.add(token);
}.bind(this));
return this;
};新手 我想请教下.bind(this) 这个有点看不懂 麻烦指点下
这个this指的那个?
@路可
this
指的就是DOMTokenList
,也可以理解为对应的DOM元素这东西超爱
classList这个新方法很赞,昨天面试的时候就被问到了,一时想不起来了。今天正好看到你的文章,受教。
有个问题想问一下哈,当使用某个方法的时候,浏览器原生的方法和通过js库加载进来的方法在调用的过程中会有差别么?有那个更快的说法么?
@玉面小飞鱼 我也不好说,没有测试过。比方说HTML5的dataset似乎还没有直接的getAttribute来的快,至于这里classList与className的速度差别,我也不确定。当然,如果JS库使用的是相同的API,自然,JS库的速度要慢一些。
写的不错, 传承博主一贯风格
“随着JS API的越来越丰富” 按照上下文理解, 应该 是 DOM 的 API
看得笑死我了,文章高手就是高手,技术和幽默两不误。
放到收藏夹里面了。