基于active,checked等状态类名的web前端交互开发

这篇文章发布于 2016年10月30日,星期日,00:00,归类于 Web综合。 阅读 41302 次, 今日 1 次 32 条评论

 

一、热身开始

在我们进行web前端开发的时候,恩,换种接地气的说法,在我们进行网页制作的时候,一定会存在各种交互效果,比方说,选中高亮,点击显示更多,选项卡切换等等。

我可以拍着胸脯说,绝对多数前端人员在实现这些交互效果的时候,是没有什么规范或者准则的。比方说点击显示更多,有些人可能直接jQuery $().show()方法,有些人可能会写个类似.more-show的类型控制显示,具体使用什么方式,用什么样的命名,完全要看网上搜到的代码是如何实现的,以及看当时的心情。

代码如产品,要想做出好产品一定要细心打磨,同样的,如果要想写的好代码也需要细心打磨。所谓打磨,就是让一些“糙”的地方变得流畅。

好产品的意义在于用户和企业受益,好代码的意义在于自己的寿命以及同事关系。

交互效果的实现方式随性,就是代码“糙”的地方,我们其实可以约定一套通用的规范或者准则,这这一块变得流畅。虽然说从产品的角度讲,意义不是很大,但是对于我们开发人员自己或者一起合作的同事而言,却是很有裨益的。因为,可以降低我们日后维护的成本,同时有其他一些非常受用的益处。需求总是要变更了,迭代总是要进行的。好的代码准则可以让你不怕需求变更,不要加班修“霸哥”。和产品成为好盆友,每天可以早早回去陪女朋友看电视,岂不很好!

我经过这些年的实践和提炼,对于交互效果的实现提炼了一套实践准则,至少在我目前这个阶段,觉得是很有裨益的,这里跟大家分享下。

如果用一句话概括下这个交互实现准则就是:完全基于active,checked等状态类名的web交互开发

二、进入正题:基于active,checked等状态类名的web交互开发

那什么是“基于active,checked等状态类名的web交互开发”呢?

我们拿最最简单的“点击显示更多”效果举例,比方说下面这种现在见的比较多的查看更新信息的效果:
更多效果截图

默认状况下,容器会设定一个高度,例如80px,然后overflow:hidden,类似下面的CSS:

.box {
    height: 80px;
    overflow: hidden;
}

此时要实现点击展开效果,其实很简单,比较偷懒的做法就是JS一步搞定:

more.onclick = function () {
    box.style.height = 'auto';
};

也有喜欢用CSS类名控制的小伙伴,此时会发挥自己在CSS命名上的天赋,来一个.height-auto的类名:

.height-auto {
    height: auto;
}

于是,JS代码变成:

more.onclick = function () {
    box.className = box.className + ' height-auto';
};

其实从产品角度讲,上面两种实现都无伤大雅,都是不错的实现,但是从代码层面讲,则均有不足之处。

1. JS直接控制样式的不足
由于我们网页大部分的样式都是CSS控制的,一旦JS也直接参与样式控制,CSS和JS就存在交叉关系,用一个软件术语描述就是“耦合了”,一旦两个事物发生耦合,势必会增加日后的维护成本。举个例子,产品上线后,设计师突然希望这里的展开不要这么的硬,希望可以有点CSS3动画效果。请问,此时,你当如何维护,是不是CSS既要修改(增加动画控制),JS也要修改(设置heightauto不会触发CSS3动画),看,要修改两个地方。如果我们一开始所有的样式都值交给CSS控制,只需要改一个CSS文件就可以了。同时,考虑到,很多公司写CSS的和写JS的不是同一个人,如果CSS和JS同时修改,岂不是要动用两个人力参与,这用人的成本和周期就上去了。虽然,计算下来,都是小成本,但是,但我们埋头某项目或者某问题的时候,被这种小需求插入,其实是很损耗精力的。如果这类小打小闹(尤其不是自己本职的)总能避免,工作岂不会轻松很多。

2. 命名语义或随意的问题
类名样式语义本无可厚非,但是,对于JS交互效果实现而言,语义化就是问题所在。语义化的类名往往代表了自身是有CSS样式的,例如,上面的.height-auto,但是,由于我们这种添加类名的行为是在JS代码中完成的,因此,本质上,

more.onclick = function () {
    box.className = box.className + ' height-auto';
};

more.onclick = function () {
    box.style.height = 'auto';
};

几乎没有任何区别。这个点很重要,大家再仔细想想,这两者是不是没有本质的区别。还是举例说明,同样的,设计师突然希望这里的展开不要这么的硬,希望可以有点CSS3动画效果。虽然说,从技术的角度讲,我们只需要修改CSS代码就可以了,但是,这个.height-auto命名…如果我们把里面的样式改成了CSS3动画相关内容,是不是就牛头不对马嘴了?是不是要去JS中把这个类名改成.height-animate之类的。看,最后还是改了两处。

另外还有一个看上去不是问题的问题,那就是,一个页面往往会有很多很多的交互效果,如果每个交互效果都有一个对应的类名控制,那岂不是JS文件中有很多样式控制的类名。很显然的,这东西一旦多了之后啊,自然也就不好维护了。

—————-低调的分隔线———–

但是,如果我们采用“基于active,checked等状态类名的web交互开发”,就不会有上面JS和样式耦合的问题。如何做呢?很简单,就是,我们控制样式的类名使用.active.checked等这种状态类名,例如:

more.onclick = function () {
    box.className = box.className + ' active';
};

并且,网页所有的页面交互都使用这种状态类名,根据我自己的实践,就算所有交互都使用.active这同一个类名都是可以的,而且事实上是推荐这么做的。

注意,下面这条准则很重要,很重要,是本文的精髓–

.active等JS交互类名自身绝对…绝对不能有CSS样式

再说一遍,自身无样式,就是一个状态标识符,用来和其他类名发生关系,然后让其他类名的样式发生变化。这种关系可以是父子,兄弟或者自身。

不急,我们先看一个例子,您可以狠狠地点击这里:基于.active类名的显示更多交互demo

默认是这样的:
更多效果截图

当我们点击更多按钮后,会以CSS3动画的方式展开剩余所有文字信息,如下动图:
.active实现展开更多动画效果gif截图

下面我们看下关键的CSS代码,这个.active是如何自身无样式的:

.box {
    max-height: 80px;
    transition: max-height .25s;
    overflow: hidden;
}
.box.active {
    max-height: 200px;
}
.active > .more {
    display: none;
}

可以看到,当我们点击按钮后,盒子变高,以及更多元素隐藏,全部都是通过和.active发生关系后发生的,而不是.active自己的样式。例如,我们盒子实现的200px以内任意高度的动画效果,是通过.box.active组合类名触发的,用中文解释就是.box元素同时有.active状态的时候,样式如何如何…

同样,.active > .more表示,当我的爸爸状态为.active的时候,我隐藏。

.active本质上扮演的角色不是类名样式,而是一个标识符!

—————-低调的分隔线———–

有人可能会疑问,为什么要这样处理呢?

还记不记得.height-auto命名问题所在,由于这个类名本身牵扯样式,因此,本质上,还是把CSS样式牵扯到了JS文件中。但是,如果是一个完全没有独立样式的状态标识符,则.active在JS中扮演的角色也就成了一个完完全全真真切切的状态占位符了!也即是说,虽然我们的JS文件中出现了类名,但是,就如同.JS_xxx类名作为DOM选择器一样,本身适合样式分离的,实现了真正意义上的样式和行为分离

比方说,技术总监突然觉得这里展开更多有动画效果太啰嗦,要直接展开,要更硬朗,需要进行改动怎么办?你会发现,我们根本无需劳烦JS开发工程师,直接页面仔改一下CSS文件就可以了,例如,回到一开始的height:auto

.box {
    height: 80px;
    overflow: hidden;
}
.box.active {
    height: auto;
}

三、高潮开始:基于active状态类名的web交互开发更炸裂的优点

采用“基于active状态类名的web交互开发”除了上面提到的“CSS样式和JS行为分离”有利于日后维护的优点外,还有很多其他炸裂的优点:

  1. 省了很多想命名的脑细胞

    妈妈再也不要担心有些单词不知道怎么翻译啦~~

  2. CSS和JS代码可读性更强了

    一旦在CSS或JS中看到'.active',大家都知道,页面这块内容要变形了;

  3. JS代码量更少了

    例如,我们在全局,或者顶层局部定义这么一个变量:

    var ACTIVE = 'active';

    则,由于我们所有的交互都只要这一个类名,因此,当我们JS压缩的时候,其压缩率要高很多,也更好维护。假如说页面中有10个UI交互效果,原先是10个不能压缩的字符串,现在变成了1个,代码量自然要少。

  4. 类名压缩成为了可能

    我几乎从未见到国内的网页产品HTML中的类名是有压缩的,如果.aaa-bbb-ccc这种类名可以压缩成1~2个字符,那岂不是CSS和HTML压缩率会更喜人,同时,我的CSS命名最佳实践(无ID,无层级,无标签)唯一的HTML中className会比较多的问题也解决了。

    类名压缩最大的阻碍,在我看来,就是我们小伙伴在实现JS交互效果的时候不注意,常常把带有CSS样式的类名混在JS文件中,并且命名随意,并且,会把类名字符串进行分隔处理,尤其一些网上的UI组件,类似:

    var classNameRoot = 'swipe-slide-';
    

    然后,通过这个类名前缀,拼接其他类名,你说,这该如何准确压缩。

    但是,如果大家遵循“基于active状态类名的web交互开发”,JS文件中只有不参与CSS样式的类名选择器,以及不参与CSS样式的交互效果占位符,则我们就可以通过简单的设置不过滤的类名,实现我们的类名压缩效果了。例如在config.js中:

    {
        "compressClassName": true,
        "ignoreClassName": ["active", "disabled", "checked", "selected"]
    }

    让我们的类名压缩工具不压缩这些类名,则我们完全不要关心JS,也就是无需去截自页面使用的JS资源,只要安安心心压缩CSS文件中的类名,同时对我们页面中使用的类名进行压缩就可以了,一个想实现很多年的东西就通过这么个实现准则约束给实现了。

    实现了?对,实现了。

    由实习生完成的我们阅文集团新的招聘官网已经可以外网访问了,不过还有些细节需要打磨,所有老站还没做301跳转,现在我是悄悄的告诉大家地址的,大家这几天低调传播,什么时候高调传播可以关注我的微博http://join.yuewen.com/

    整个招聘站的HTML类名(如果CSS有使用)全部进行了2字符的压缩(2字符显得更规整,我心里舒服),如下截图:
    类名压缩后的HTML效果截图

    经测试,CSS大小可以降低10%,HTML则受标签多还是内容多影响,也是有明显的体积减小的。

四、缓缓收尾:为何以前没有这样的实践准则

“基于active状态类名的web交互开发”在多年之前实际上是很难实践下去的,因为,该准则依赖于的CSS关系符选择器,而几乎所有的CSS关系符选择器都是从IE7浏览器开始才支持完全的。

例如:.active.xxx之前在IE6下是有bug的,顺序没放好,可能会发生.active冲突的情况;相邻父子关系.active > .xxxIE7浏览器才开始支持,由于我们网站几乎所有普通交互都使用这同一个.active类名,因此,不能简单使用父子选择器.active .xxx有一定概率会发生冲突,使用相邻父子关系.active > .xxx约束则无此问题;相邻兄弟选择器.active + .xxx以及普通兄弟选择器.active ~ .xxx全部都是IE7浏览器才开始支持。也就是,浏览器的发展催着这这种更好实践的成型和普及。不好意思,说错了,应该还没普及,我是希望可以从本文开始慢慢普及。

我想,现在基本上很少有公司还要兼容IE6的了,如果你的产品还要做这样的事情,我只能扼腕而叹了。

五、缓缓收尾:我常用的一些状态类名

我简单回顾了下,我使用的状态类名最多的就是.active以及.disabled,在单复选框UI组件使用过checked,自定义下拉使用过selected等,其他几乎鲜有使用。

另外,还有其他一些状态类名,当然,是我见别人用过的,有.on,以及.in.out。前面的.on等同于我习惯使用的.active,因为.on的状态含义更广,例如单选框选中用.on表示就挺好,但是如果是.active则似乎则是另外的意思了,所以,如果大家希望偷懒,建议可以试试.on这个状态标识符,真正的可以贯穿所有交互的状态类名;后面的.in.out是用来标示CSS3 animation动画状态的,也属于状态类名。

等等。

六、最终收尾

中国有句成语,就做“立竿见影”,比喻效果很明显。

如果从降低维护成本的角度讲,折腾那些node前端工具流所节约的开发和维护成本确实是立竿见影,相比之下,本文的这点东西(如果不是后面的类名压缩撑腰)节约的开发和维护成本简直就是不值一提。

这其实是一件很悖论的东西。从企业的角度讲,自然是希望员工能够把好钢用在刀刃上,以最小的力带来最大的收益,优先解决最为瓶颈的东西上,所以,如果大家比较看重职级,抬头,薪资这样的东西,建议更多精力花在这些“立竿见影”的事情上,本文的这些内容呢,大可不必较真。但是,从工程师的角度讲,指有责任心的工程师,会对技术和代码品质很有追求,会化很多精力去磨代码,这其实是很棒的一件事情,就好比本文所说的这些代码准则。然而,问题在于,工程师花了很多精力做这样的事情,其带来的价值往往并不是立竿见影的那种,需要时间来证明。从企业的角度讲,这样的工程师并不一定是最想要的工程师,尤其中小企业。企业属于商业机构,不是学术交流场所,看重利益,所以,很多做技术的同学一定要认清这一点,如果你在意公司对你的认可,做出立竿见影的成绩来说话;如果你觉得自己技术很不错,但升职加薪的不是我,想想看是不是自己的认知层出现了偏差,是不是认为代码最棒的就应该工资最高。当然,如果你就是希望技术纯青,对世俗这些不care,则建议你要一直坚定的走下去,每天成长每天提高,通过技术给企业给同事给同行带来更加沉稳更加厚重的收益,假以时日,你一定可以走出和别人不一样的康庄大道,要知道,决定一个人最终成功的不是智商,而是毅力。这些话既是鼓励也是自勉。

对了,对于前文提到的我自己写的类名压缩工具,目前暂不开放,除了精力有限的原因外,最主要原因是……想了想,归根结底还是精力有限,嫌麻烦。你看我10月份就2篇文章,忙的连鱼缸的鱼都没时间喂。

恩,就这些,欢迎交流!

(本篇完)

分享到:


发表评论(目前32 条评论)

  1. Charles说道:

    误打误撞看到了这篇文章,这个思路确实解决了一个困扰我很久的问题,非常感谢

  2. wl22说道:

    我一直是用w-ac,w-di,w-first,w-last这样的类控制。

  3. 风儿寻说道:

    为什么我写了一遍,没有展开效果呢

  4. 小小说道:

    大神,你用的那个压缩工具呢?求教!

  5. 韩长寿博客说道:

    不错支持

  6. 小儿来二斤酱牛肉说道:

    楼主的想法挺好,用一个类来做状态,通过父子 兄弟关系来 改变一些交互效果,但是对于一下简单的交互,例如展开,隐藏等,楼主还是用到了js来控制(增加click事件),这算不算 “耦合”,对于这些 简单的交互效果, 我觉得 不用js来做 更好,完全由html标签和css样式 来控制交互。
    我是用 这个标签 来做,通过控制checked,来关联 父子 和 兄弟样式,然后把他透明度设置为0 来实现点击切换,

  7. 旧时长安说道:

    这是第一次评论,之前在写效果的时候,自己也用到了本文所提到的方法,但是没有系统的整理过,今天,看了这篇文章,感觉思路更清晰了

  8. 墨染说道:

    写得非常好,,要是早点知道就可以省很多麻烦了,,有时候写某些效果纠结的不行,,用这个就好多了

  9. 浅唱姩華说道:

    受益颇多

  10. 文艺小青年说道:

    鱼哥 ,你的产量越来越少了!!!

  11. yanghaha说道:

    好产品的意义在于用户和企业受益,好代码的意义在于自己的寿命以及同事关系。

  12. simon_tal说道:

    换成 IE8 胡歌都不见了 哈哈!

  13. yp说道:

    偷偷用IE7/8测新的招聘官网一下,你懂的~

  14. xieguanglei说道:

    拨云见日!

  15. 猫哥说道:

    不单独在 active 上写样式是点睛之笔!

    类名压缩这块有些不一样的看法,因为类名可能同时有 CSS、JS和模板在引用,所以使用场景上会有很多限制,配置也会越发复杂。从成本收益上看,遵循一定的命名规范即可,我猜测 gzip 的压缩收益可能会更好。

  16. cshenger说道:

    然而看了下网站,你们居然不招前端?!差评>_<

  17. 清冰说道:

    有个项目的购物车就用了ckecked,初衷是为了方便通过标识选中的商品调整页面显示的相关数据,还是张大大考虑的深远,讲解的透彻,赞~

  18. zsusyt说道:

    学习了。

  19. 盖大楼说道:

    学习了,看完了总结了下。自我感觉active就是一个标志,在不同的父级下,active代表的含义不同,可以使改变任何状态。压缩类名是不是就是,在js中用一个var ac=’空格active’,ac代替要出现active的地方。
    有个地方大神写错字了:三_3.加入说页面中有10个UI交互效果。
    我自己模仿写了一个demo:active在不同的父级box及box2下有不同的效果:
    box:下拉更多;box2:字体变红;
    用ac代替active,但是定义变量时要在“ active”前面加个空格。

    .box{padding: 10px; font-size: 14px; max-height: 90px; overflow: hidden; transition: all .25s;width: 260px;float: left; }
    .box2{float: right;}
    .box>p,h3{width: 260px;}
    .more{background:linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.9) 50%, hsla(0,0%,100%,1)); display:block ; width: 270px; height: 80px;line-height: 80px; text-align: center; position: absolute; top:70px; left: 0px;color: black; text-decoration: none;}
    .box.active{max-height: 200px;}
    .box2.active{color:red;}
    .active .more{display: none; }

    flv.js-B站HTML5播放器内核开源
    项目地址:https://github.com/Bilibili/flv.js
    一个纯JS的不依赖Flash的HTML5 Flash视频 (FLV)播放器!
    本项目受hls.js启发,需要依赖Media Source Extensions才能生效。
    更多↓

    字体变色

    var box=document.getElementById(‘box’);
    var box2=document.getElementById(‘box2’);
    var more=document.getElementById(‘more’);
    var ac=” active”;
    if(box&&box2&&more){
    more.onclick=function(){
    box.className+=ac;
    box2.className+=ac;
    };
    }

  20. 会飞的胖子说道:

    之前是在.on上面直接进行css,没有考虑到耦合的问题,看了文章后才发觉这样的更改问题还是很多,懒,比较多用的是class“.on”

  21. 麦尼玛桑说道:

    选项卡就用这种方式实现的,但是没像大神一样想这么深入系统,膜拜

  22. erlking说道:

    类名压缩的概念真是耳目一新,孤陋寡闻貌似没见过有这类的自动化插件,期待大湿放出

  23. ziven27说道:

    两个字符的如果不加前缀很容易和你的zxx.lib.css冲突。比如.dn, .di。
    对于这种不能独立使用的的样式名,我之前习惯加一个s_(son_)的前缀。
    这样就可以放心的使用.s_on了。
    有一个疑问就是,我可以直接使用下滑线作为css名称的前缀吗?
    比如: .box._on{ } .box ._on{ }
    这样就变成了_on,这种命名直接给人的感觉就是前面需要一个父级或者兄弟的选择器。就是没有详细测试过下滑线前缀,不敢放心的用。

  24. dals说道:

    路过

  25. XXig说道:

    受益匪浅,偷偷的说阅文集团新的招聘官网,缩小窗口轮播图旁边会留白哦

  26. meepo说道:

    学习。

    旭哥文章满满的干货。

  27. 7P_LonG说道:

    on,off,loading, 个人还会用上这些,当然只是脑中觉得方便,没想到这么深。
    受教了。

  28. cc长空说道:

    我常用的状态类是`.Act`,首字母大写来跟其他普通类区分。
    另外`Act`莫名的感觉更帅一点

  29. RDD说道:

    大赞,确实是优秀的解决方式!各种程度上都受教了!

  30. 前端-小强说道:

    赞,向张老师学习。简单快速完成任务,也许是对产品快速迭代有更多帮助,但是多花点时间在思考上面,有没有更合理的方法,而不是一味的简单完成任务,对于整个项目的维护会更有帮助。