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