常用属性选择器-灵活布局

您是否碰到过这样的一种需求,在CSS布局中,有一个元素,它有很多种的状态,不同的状态,有不同的CSS样式,并且不同的状态,有不同的点击处理。

举例

接下来,我们就按照一个简单的例子来说一下,有一个按钮元素,它有七种颜色状态(假设为彩虹七色吧),而每一种颜色状态,被点击之后的操作,其实是不同的,我们这里假设点击后的操作,只是简单的alert出当前的颜色。

那么最直接的实现方式就是,写7个单独的CSS,例如:


.hongse{
    background-color:#f00;
}

.chengse{
    background-color:#FF7F00;
}

.huangse{
    background-color:#FFFF00;
}

.lvse{
    background-color:#00FF00;
}

.qingse{
    background-color:#00FFFF;
}

.lanse{
    background-color:#0000FF;
}

.zise{
    background-color:#8B00FF;
}

然后在满足一定的条件的时候,给对应的元素,添加对于的className即可。

确实,在一定程度上,这样就已经完成了我们得需求

对了,还有对每一种颜色的按钮点击之后,就要执行对应的回调,那么就是,依然可以很简单的做出这个处理。


//wrapper为点击区域的父元素
function _hongse(){
    alert("hongse");
}
wrapper.on("click",".hongse",_hongse);

很简单的,我们直接使用jQuery的写法,就可以完成了,而且条理清晰,罗列有序,请查看示例:使用className布局

OK,这一阶段到这里基本就算是结束了。

需求变更

当项目执行了一段时间之后,出现一个新的需求,当点击一个颜色的按钮之后,根据响应的不同,把该按钮改为其他的颜色。

比如,这里拿最简单的例子来说,当点击之后,颜色变为七彩的下一种颜色,即:红色-橙色-黄色-绿色-青色-蓝色-紫色-红色。

以这个循环色来做处理。于是,可以在前面示例的基础上,加上以下的处理:

添加一个链接的状态符


nextLink = {
    "hongse" : "chengse",
    "chengse": "huangse",
    "huangse":"lvse",
    "lvse":"qingse",
    "qingse":"lanse",
    "lanse":"zise",
    "zise":"hongse"
};

//在每一个回调处理的时候,根据当前的状态,找到下一个状态
function _hongse(){
    var obj = $(this),
        colorType = "hongse";

    obj.removeClass(colorType).addClass(nextLink[colorType]);

}
wrapper.on("click",".hongse",_hongse);

参考示例:第一次需求变更

OK,这样也完全可以解决我们的需求。

需求变更

在前面的需求中,变化是有序的,而现在,需求继续变更,我任何一种颜色的按钮被点击之后,会变为另外一种颜色,而变化的颜色是七彩中的任意一个。

那么上面的解决方案,就变得有些问题了,比如:nextLink中的链接其实就没有意义了,所以需要去除掉。所以需要做以下的修改:


var colorArr = ["hongse","chengse","huangse","lvse","qingse","lanse","zise"],
    colorStr = " "+colorArr.join(" ")+" ",
    colorLen = colorArr.length;

//_changeColor的一种解决方案
function _changeColor(){
    var obj = $(this),
        className = obj.attr("class"),
        classArr = className.split(" "),
        i = 0,
        len = classArr.length,
        newClass = "";

    for(i;i<len;i++){
        if(colorStr.indexOf(" "+classArr[i]+" ") != -1){
            obj.removeClass(classArr[i]);
        }
    }

    newClass = colorArr[Math.floor(Math.random()*colorLen)];

    console.log("newClass="+newClass);

    obj.addClass(newClass);

}
wrapper.on("click",".btn",_changeColor);

因为既然所有的点击之后的操作结果,已经是完成一样的逻辑了,那么就没有必要使用多次事件委托了,所以就直接使用事件委托,绑定在一个统一的选择符上面,如上。

示例:第二次需求变更的DEMO

当然,上面的解决方案,其实在我看来,是比较好的,因为这样如果后期再有需求变更,需要给元素添加额外的className的时候,不需要再修改JS的逻辑,如果不考虑这方面的原因,那么还可以有一个更暴力的解决方案,如下:


//还有一种更暴力的解决方案
function _changeColor(){
    var obj = $(this),
        newClass = "";

    newClass = colorArr[Math.floor(Math.random()*colorLen)];

    console.log("newClass1="+newClass);

    obj.attr("class","btn "+newClass);

}
wrapper.on("click",".btn",_changeColor);


示例:暴力解决方案DEMO

暴力解决方案的缺点就在于,如果之后要添加新的class样式呢?在没有点击元素的时候,是好着的,但是当点击一次元素之后,就会把新添加的className给拉下了,所以就必须要修改JS中的代码了。所以这种方案虽然是简单,但是后期维护成本太高,而且容易出错,所以并不适合的。

更简单的方法

这里,你是否想到一个更简单的方法呢?属性选择器,属性选择器的特点是,它的取值是唯一的,如果元素的样式状态需要多变的话,属性选择器是一个最适合的解决方案,因为它完全不需要管,之前的状态是什么,只要我想要把状态设置为某一状态,可以直接设置该属性的属性值即可。

而且,您完全不需要担心属性选择器的兼容问题:CSS 属性选择器详解,这是CSS2中,就引入的。

看看,如果按照属性选择器来处理的话,上面的需求,怎么实现呢?

CSS 部分,需要做一些修改:


.btn[data-color="hongse"]{
    background-color:#f00;
}

.btn[data-color="chengse"]{
    background-color:#FF7F00;
}

.btn[data-color="huangse"]{
    background-color:#FFFF00;
}

.btn[data-color="lvse"]{
    background-color:#00FF00;
}

.btn[data-color="qingse"]{
    background-color:#00FFFF;
}

.btn[data-color="lanse"]{
    background-color:#0000FF;
}

.btn[data-color="zise"]{
    background-color:#8B00FF;
}

JS部分的处理,也是很简单的,跟前面的暴力解决方案,同样简单的哦,但是这里却一点也不暴力


var colorArr = ["hongse","chengse","huangse","lvse","qingse","lanse","zise"],
    colorLen = colorArr.length;

//还有一种更暴力的解决方案
function _changeColor(){
    var newColor = colorArr[Math.floor(Math.random()*colorLen)];

    console.log("newColor="+newColor);

    $(this).attr("data-color",newColor);

}
wrapper.on("click",".btn",_changeColor);


对么?

尤其是当情况变得更复杂的情况下,而且扩展性特别高,就比如这里点击之后的回调,如果这样写好之后,在以后除非有非常大的需求变更,否则不管是添加,减少,样式变化等,都不需求再更改这一部分代码了。

而且属性选择器,配合JS,还可以做更多的,更优秀的解决方案哦,比如使用事件委托过多,会导致代码性能的降低,但是如果我使用属性选择器,那么就可以把多个事件委托,合并到一个事件委托中去,大大的提升了代码的性能,特别适合于大型复杂的门户网站哦,关于如何解决这个问题,可以参考:深入理解-事件委托

示例:优雅解决方案DEMO

总结

虽然很多公司或者说团队,都是分重构和JS的,但是在我觉得,一个好的前端,需要都考虑好,才能写出最好的代码,如果只考虑重构,也许会让写JS的人,工作量加大,因为么有考虑到这块的功能逻辑,自己重构的HTML结果,CSS的结构,是否适合这里要做的JS逻辑呢。

综合考虑,纵观全局,才能写出更好的代码。

好了,说到这里就结束了,欢迎交流,如果发现文中的问题,请指正,谢谢!

本文属于原创文章,转载请注明出处,谢谢!

本文地址:http://www.zhangyunling.com/?p=575

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>