前言
上次和大家聊过 background系列之你不知道的background-position,向大家展示了里面的一些小细节和未被发掘出来的特征。应该说 background
是一个很有意思的东西,不仅实际使用的频度超高,而且处处充满着好玩的妙趣。
老需求
这里并不打算和大家聊所谓的最佳实践,更多的是希望可以通过一些示例激起大家更深层次的思考和探索欲。
大家来看下面这个例子,应该算是iOS
中的典型列表风格,如图0
:
从图0
我们能看到这个列表中每个子项都有一根底边线,假设子项高为40px
,并且除了最后一根边线是全封闭的外,其余的边线都是非封闭型,距左侧有一定的空白间隙;需要注意的是,当某个子项被选中或激活时,其反馈区域是整行,未封闭区域的背景色也需要改变,如图0
中的第4行效果所示。
我们来细化一下需求:
- 每个子项都需要底边线;
- 除了最后一根边线,其余边线都是非封闭的;
- 子项被选中或激活时,响应区域为整行;
假设这就是我们现在的需求,你会如何实现呢?
老思路
我知道,通常来说,大家首先会想到的应该是border
属性,然后配合margin
来做这件事。
于是我们可能会这样来写HTML:
HTML:
1 2 3 4 5 6 7 8 |
<ul class="demo"> <li>Lady gaga</li> <li>Mariah Carey</li> <li>Adele</li> <li>Avril Lavigne</li> <li>Sarah Brightman</li> <li>Celine Dion</li> </ul> |
为了实现需求第1点,CSS这样写:
CSS:
1 2 3 4 |
.demo li { border-bottom: 1px solid #ccc; line-height: 40px; } |
Cool,简直太简单了。然后我们又接着来实现第2点:
CSS:
1 2 3 4 5 6 7 |
.demo li { margin-left: 15px; } .demo li:last-child { margin-left: 0; padding-left: 15px; } |
第2点好像也没有想象中那么复杂,就是需要把代码写恶心点,重置最后一个子项的margin-left
,并换用padding-left
,但貌似也还可以接受,毕竟只有2行代码。
一鼓作气,我们需要继续把第3点也给做了,为了更好的理解,这里把选中或激活
的状态用经过
替代,其实是类似的,于是继续写:
CSS:
1 2 3 |
.demo li:hover { background-color: #f3f3f3; } |
迫不及待要看看这样写是否能符合预期了,如下图1
:
开心的是,需求中的第1,2两点都做到了;难受的是,第3点没达成。从图1
我们就能看到,响应区域除了最后一项外,其它的项都不是整行的。可以直接来观看这个例子, Demo1
使用border和margin做iOS风格列表
很明显,这个解决方案失败了。败在了margin
区域无法承载background
,因为margin
永远是透明的,围绕在盒子之外。有兴趣的童鞋可以翻翻规范了解这些信息。
再出发
虽然说方案1
有问题,但我们也知道了,如果是使用margin
来做的间隙,那么将无法填充背景,而padding
是可以的,那么这就可以作为下个方案的指导。
有了这样的指导,我们是不是可以考虑将内容的间隙和边线的间隙分开处理?内容仍然使用padding
,边线额外处理,互不影响,避免出现方案1
的情况。
不论怎样,我相信在大多数时候,你都不想为了达到某个效果,而增加层级。所以这里,不在之前HTML
上做任何改变,那么要单独处理边线,我们就只能给它创造一个伪元素
了。
于是我们又开始一一实现,首先,内容要有间隙:
CSS:
1 2 3 4 |
.demo li { padding-left: 15px; line-height: 40px; } |
接下来,每个子项都要有底边线。说好的伪元素
,我们不能抛弃:
CSS:
1 2 3 4 5 6 7 8 |
.demo li::after { position: absolute; right: 0; bottom: 0; left: 0; border-bottom: 1px solid #ccc; content: "020"; } |
我们还需要让出了最后一根边线外的其它边线距左侧有间隙:
CSS:
1 2 3 |
.demo li:not(:last-child)::after { left: 15px; } |
就剩最后一点了,来给它们加上响应反馈效果:
CSS:
1 2 3 |
.demo li:hover { background-color: #f3f3f3; } |
嗯,差不多了。貌似还差点,我们的边线的是绝对定位的,所以还需要给它的包含块加上定位:
CSS:
1 2 3 |
.demo li { position: relative; } |
这下看起来是处理得差不多了,让我们来瞧瞧实际的效果,如图2
:
Good job! 方案2
完全实现我们的预期,可以看具体的示例:Demo2
使用伪元素做iOS风格列表
精益求精
方案2
虽然已经完全实现了需求,但我总是有点不太喜欢它。因为我发现,当内容的边距修改后,边线的边距也需要手动修改,问题就在于它们被分别处理了;而且代码居然超过10行了。
所以,如果我不想只要改变边距就得改2处地方,还是只能讲它们合起来处理,不再分开。这意味着伪元素
不再是我们需要考虑的,包括方案1
中的margin
也要排除。
怎么办?貌似有点棘手了。
可能大家已经猜到我会把注意力放到background
上了,毕竟好像这个系列讲的就是background
,但不知道会不会猜到我准备怎么做。
旧酒新衣
用background
做边线这样的方案,不少童鞋肯定遇上过,比如说:菜单之间的分隔竖线(当然,这个能用几十种方式来做),这个比较简单,或用背景图,或用伪元素承载背景色。
然后在当前这个需求背景下,这些方式都不适用了。显然,我不会在真实的背景图片方向去考虑这个问题,毕竟我懒嘛,并不想切图。
所以,我得打打渐变背景的主意了,大家知道我们其实是可以用渐变来画背景图像的。于是,我又开始写了,当然,HTML
仍然不变:
CSS:
1 2 3 4 5 |
.demo li { padding-left: 15px; line-height: 40px; background: linear-gradient(#ccc, #ccc) no-repeat; } |
好了,内容的边距有了;渐变背景也有了(虽然是纯色的),但它不是线,不是线。得改改,我们是不是可以让渐变从透明渐变到边线的颜色呢?试试:
CSS:
1 2 3 4 5 |
.demo li { padding-left: 15px; line-height: 40px; background: linear-gradient(transparent 39px, #ccc 39px, #ccc) no-repeat; } |
我们让渐变背景从transparent
渐变到#ccc
,transparent
区域占39px
,剩下1px
给#ccc
。
事实上,这是可行的,运行结果如我所想,但我还要处理一下边线距左侧的边距问题和响应反馈:
CSS:
1 2 3 4 5 6 |
.demo li:not(:last-child) { background-position: 15px; } .demo li:hover { background-color: #f3f3f3; } |
搞定了,赶紧看下效果,如图3
:
结果确实正如我想,可以看具体的示例:Demo3
使用渐变背景做iOS风格列表
新的突破
通过方案3
的实现可以发现使用渐变背景来做边线是完全可用性,只是它也并没有解决我想要的只改1处
边距就同时处理了内容边距和边线边距。
所以就只能再改改了,再发掘发掘background
的其他魅力。
我知道,background
体系下有一个特性是用来控制背景图的渲染起始位置的,它可以根据设置,让背景图从边框
,内补白
或内容
区域开始渲染。
是的,你想起了,它是background-origin
。
因为最后一根边线是封闭的,无需再做任何处理了。但如果我们需要完成其它边线的边距随内容的边距变化而变化,那么就需要让边线从元素的内补白
以内的区域开始渲染,即内容
区域,于是我们这么改:
CSS:
1 2 3 |
.demo li:not(:last-child) { background-origin: content-box; } |
是的,就是这么一句简单的改动,我想要的都得到了。之后边线的边距将由内容的边距来决定,当我们将内容的边距改成20px
时,边线的边距也将自动变成20px
(当然,原因是因为背景图从元素的content-box
区域开始渲染)。
看看我们新突破后的成果吧,如图4
:
这是属于我们共同的胜利,可以看具体的示例:Demo4
使用渐变背景和背景图起始位置做iOS风格列表
这只是background
趣味性的一个小点,后续应该会继续写一些。
写在最后
解决方案不是永远靠谱的,任何差异化的场景都不一定能复用以往的经验,但不停的探索是可以的,并且它是建立在扎实的基础上的。enjoy it.