预处理器循环不会在空间中引起剧烈的爆炸(我希望),但它们对于编写DRY CSS非常有用。 尽管大家都在谈论的样式库和模块化设计,大部分重点一直在CSS选择器这上面。 不管你选择什么样的缩写方式(BEM,OOCSS,SMACSS,ETC),循环可以帮助保持你的样式更具可读性和可维护性,直接加工到你的代码中。
我们将看看循环可以做什么,以及如何在主要的CSS预处理器中使用它们:Sass,Less和Stylus。 每种语言都提供了一种独特的语法,但他们都完成了工作。 有不止一种方法来循环一只猫。
猫
PostCSS也很受欢迎,但它不提供它自己的任何语法。 虽然它有时被称为后处理器,我称之为元预处理器。 PostCSS允许您编写和共享自己的预处理器语法。 如果你想,你可以重写Sass或Less在PostCSS,但有人已经先行一步了。
循环条件
无限循环是一件很可怕的事情。 如果你不小心,无限循环可能会减慢或让你的编译器崩溃。 这就是为什么循环应该总是提供限制条件的目的 —— 通常由多个增量重复或对象集合定义。
在编程术语中:
- while循环是通用的,并且在满足任何条件时将保持循环。 小心! 这是无限循环最可能的地方。
- For循环是增量式的,对于特定数量的重复运行。
- For-Each循环遍历集合或列表,一次一个地遍历每个项目。
每种类型的循环比前面更细粒度。 一个for-each
循环只是一种for
循环,这也是一种while
循环。 但是大多数的用例都属于更具体的类别。 我很难在其他条件下寻找真正的while
循环 —— 大多数例子可以更好地处理for
或for-each
。 这可能是为什么Stylus
只提供后者的语法。 Sass为三种循环方式都提供了独特的语法,Less在技术上根本没有循环语法 - 但这不会阻止我们! 让我们深入探讨一下。
for-each循环
预处理器循环在您有要循环的项目(列表或数组)(如社交媒体图标和颜色数组)或状态修饰符列表(success
,warning
,error
等)时最有用。 因为for-each
循环被绑定到已知的项目集合,它们往往是最具体和可理解的循环。
让我们从循环一个简单的颜色列表开始,看看它是如何工作的。
在Sass中,我们将使用@each指令(@each $item in $list)来获取颜色:
<div class="darkslateblue"></div>
<div class="mediumorchid"></div>
<div class="seagreen"></div>
<div class="steelblue"></div>
scss:
// colors
$colors: darkslateblue mediumorchid seagreen steelblue;
// loop!
@each $color in $colors {
.#{$color} {
background: $color;
}
}
// styles
body {
display: flex;
}
div {
flex: 1 1 auto;
height: 100vh;
}
在Stylus中,语法(for item in list)处理集合:
HTML:
<div class="darkslateblue"></div>
<div class="mediumorchid"></div>
<div class="seagreen"></div>
<div class="steelblue"></div>
Stylus
HTML Stylus Result
EDIT ON
// colors
colors = 'darkslateblue' 'mediumorchid' 'seagreen' 'steelblue'
// loop!
for color in colors
{'.' + color}
background: unquote(color)
// styles
body {
display: flex;
}
div {
flex: 1 1 auto;
height: 100vh;
}
VIEW COMPILED RERUN
Less不提供循环语法,但我们可以使用递归来模拟它。 递归是当从内部调用函数或mixin中发生的。 在Less中,我们可以使用mixins进行递归:
.recursion() {
/* an infinite recursive loop! */
.recursion();
}
现在我们添加一个guard
到mixin
,以防止它无限循环。
.recursion() when (@conditions) {
/* a conditional recursive "while" loop! */
.recursion();
}
只要满足条件(@i <= length(@list)),我们就可以通过添加一个从1开始的计数器(@i) - 其中length(@list)将我们的循环迭代限制为与我们的集合相同的长度。 如果我们在每次传递中提取下一个列表项,我们将有一个手动的for-each循环:
<div class="darkslateblue"></div>
<div class="mediumorchid"></div>
<div class="seagreen"></div>
<div class="steelblue"></div>
// colors
@colors: darkslateblue mediumorchid seagreen steelblue;
// loop definition
.backgrounds(@list, @i: 1) when (@i <= length(@list)) {
// extract the right color from the list
@color: extract(@list, @i);
// apply the background to the selector
.@{color} {
background: @color;
}
// recursive call for the next color
.backgrounds(@list, @i + 1);
}
// application
.backgrounds(@colors);
// styles
body {
display: flex;
}
div {
flex: 1 1 auto;
height: 100vh;
}
可以看出在Less中实现这一切是很困难的一种方式。
社交媒体按钮
循环遍历列表可能很有用,但更多时候你想循环遍历对象。 一个常见的例子是为社交媒体按钮分配不同的颜色和图标。 对于列表中的每个项目,我们需要网站的名称和该社交网络的品牌颜色:
SCSS:
$social: (
'facebook': #3b5999,
'twitter': #55acee,
'linkedin': #0077B5,
'google': #dd4b39,
);
使用Sass
,我们可以使用语法@each $ key
,$value in $array
访问每个对的键(网络名称)和值(品牌颜色)。 这是完整的循环:
HTML SCSS Result
EDIT ON
<ul class="social">
<li>
<a href="http://www.facebook.com/me/">
Facebook
</a>
</li>
<li>
<a href="http://www.twitter.com/me/">
Twitter
</a>
</li>
<li>
<a href="http://www.linkedin.com/me/">
LinkedIn
</a>
</li>
<li>
<a href="http://plus.google.com/me/">
Google+
</a>
</li>
</ul>
SCSS
// establish social media colors
$social: (
'facebook': #3b5999,
'twitter': #55acee,
'linkedin': #0077B5,
'google': #dd4b39,
);
// loop, to style social media links
@each $name, $color in $social {
// selector based on href name
[href*='#{$name}'] {
background: $color;
&::before {
content: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/15542/#{$name}.png);
}
}
}
// styles
ul {
margin: 0 auto;
max-width: 30rem;
}
a {
border: 4px solid black;
border-radius: 6px;
color: black;
display: block;
font-weight: bold;
margin: 0.25rem;
padding: 0.5rem;
text-decoration: none;
&::before {
display: inline-block;
vertical-align: middle;
margin: 0 0.5em;
}
}
Stylus 也有同样的语法:for key, value in array
<ul class="social">
<li><a href="http://www.facebook.com/me/">Facebook</a></li>
<li><a href="http://www.twitter.com/me/">Twitter</a></li>
<li><a href="http://www.linkedin.com/me/">LinkedIn</a></li>
<li><a href="http://plus.google.com/me/">Google+</a></li>
</ul>
Stylus:
// establish social media color
ssocial = {
'facebook': #3b5999,
'twitter': #55acee,
'linkedin': #0077B5,
'google': #dd4b39,
}
// loop, to style social media links
for name, color in social
// selector based on href name
[href*={name}]
background: color
&::before
content: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/15542/' + name + '.png')
// styles
ul {
margin: 0 auto;
max-width: 30rem;
}
a {
border: 4px solid black;
border-radius: 6px;
color: black;
display: block;
font-weight: bold;margin: 0.25rem;
padding: 0.5rem;
text-decoration: none;
&::before {
display: inline-block;
vertical-align: middle;
margin: 0 0.5em;
}
}
在Less中,我们必须手动提取对的每一边:
html
HTML LESS Result
EDIT ON
<ul class="social">
<li>
<a href="http://www.facebook.com/me/">
Facebook
</a>
</li>
<li>
<a href="http://www.twitter.com/me/">
Twitter
</a>
</li>
<li>
<a href="http://www.linkedin.com/me/">
LinkedIn
</a>
</li>
<li>
<a href="http://plus.google.com/me/">
Google+
</a>
</li>
</ul>
LESS:
HTML LESS Result
EDIT ON
// establish social media colors
@social:
'facebook' #3b5999,
'twitter' #55acee,
'linkedin' #0077B5,
'google' #dd4b39,
;
// for loop to iterate over array
.each(@array, @i: 1) when (@i <= length(@array)) {
// extract social names and colors
@pair: extract(@array, @i);
@name: extract(@pair, 1);
@color: extract(@pair, 2);
// selector based on href name
[href*='@{name}'] {
background: @color;
&::before {
content: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/15542/@{name}.png');
}
}
.each(@array, @i + 1);
}
// application
.each(@social);
// styles
ul {
margin: 0 auto;
max-width: 30rem;
}
a {
border: 4px solid black;
border-radius: 6px;
color: black;
display: block;
font-weight: bold;
margin: 0.25rem;
padding: 0.5rem;
text-decoration: none;
&::before {
display: inline-block;
vertical-align: middle;
margin: 0 0.5em;
}
}
循环增量
For循环可以运行重复任意数量,而不只是对象的长度。 您可以使用它来创建网格布局(从1到12的列),循环通过色轮(色调从1到360),或者为具有nth-child
和div
生成内容编号。
让我们开始一个循环生成36个div
元素,每个div
提供数字和背景颜色,使用:nth-child
。
Sass提供了一个特殊的for循环语法:@for $ count
从$ start
到$ finish
,其中$ start
和$ finish
都是整数。 如果起始值较大,Sass将向下计数而不是向上计数。
代码展位
through
关键字意味着我们的循环将包括数字36.您还可以使用to
关键字,其中不包括最终计数器:@for $i from 1 to 36
将只循环35次。
Stylus具有与递增类似的语法,但是to
和through
分别替换为...
和..
代码展位
Stylus还提供了一个range()
函数,它允许您更改一个增量的大小。 在范围(0,360,10)中使用色调将在每次重复时将计数增加10。
Less必须再次使用递归混合宏(recursive mixins)去实现。 我们可以为迭代次数(@i
)创建一个参数,使用 when(@i> 0)
限制条件,并在每次迭代时减去一个 —— 以使其像一个递减的for-loop:
代码展位
值得注意的是CSS
在没有使用预处理器的情况下也可以给我们第n个子节点编号。 虽然CSS没有循环结构,但它确实提供了一个counter()
,可以根据任何数量的DOM相关条件进行递增,并在生成的内容中使用。 很遗憾,它不能在content
属性之外使用,因此我们的背景颜色不适用:
代码展位
每个网格跨度是一个百分比,使用数学span / context * 100%
——所有网格系统必须进行的基本计算。 这里再次用Stylus和Less实现下:
代码展位
代码展位
定制头像
在OddBird,我们最近设计了一个带有默认用户头像的应用程序 - 但我们希望默认值尽可能的唯一。 最后,我们只设计了九个独特的图标,并使用循环将它们转换成1296个不同的头像,所以大多数用户永远不会看到重复的头像。
每个头像有五个属性:
代码展位
- 起始图标形状(9个选项)
- 旋转0,90,180或270度(4个选项)
- 深色填充(6选项)
- 浅色背景(6选项)
- 反转颜色的true / false属性(2个选项)
代码有六种颜色和三个循环:
@for $i from 0 through 3
给我们四个旋转
@for $i from 1 through length($colors)
允许我们循环遍历颜色列表($ colors
),并为每个颜色分配一个数字($
i)。 通常我会使用@each
循环遍历颜色的集合,但是@for
是更简单的,当我需要一个数字,每个项目以及。
嵌套的@each $ reverse(true,false)
为我们提供了为每个颜色组合翻转前景和背景的选项。
它最终在Sass中的结果为:
代码展位
将它转换为Less
和Stylus
都是一样的套路,不再去过多展示了。
通用while循环
真正的while
循环很少,但我偶尔使用它们。 我发现它们最有用的,当我顺着一个路径,看看它的根。 我不想循环遍历整个集合或特定数量的迭代 - 我想保持循环,直到我找到我想要的。 这是我在我的抽象工具包中使用的东西,但不是你在日常使用中非常需要的东西。
我构建了一个工具包来帮助我在Sass中存储和操作颜色。 在变量中存储颜色可能是任何预处理器最常见的用例。 大多数人做这样的事情:
代码展位
我知道粉红色可能不是你的网站上唯一的颜色,但它是现在唯一一个现在就需要的。 我给它多个名称,因为它对于建立抽象层是非常有用的 - 从简单的颜色(粉红色),更广泛的模式(品牌主)和具体的用例(站点背景)。 我还想把单个颜色的列表转换成我的预处理器可以理解的调色板。 我需要一种方式去表达这些值的相关性,并且是该模式的一部分。 这样做,我在一个单独的Sass地图存储所有我需要的主题颜色和键值对:
为何这样做? 我这样做,因为我可以用一个单一的变量指向我的样式指南生成器,并自动创建一个保持更新的调色板。 但是权衡利弊,这对于每个人来说并不是都是正确解决方案。 这个样式图不允许像我可以用变量一样在对之间进行直接赋值。 我需要一个while
循环来跟踪键名的痕迹,以便找到每种颜色的值:
展位图
我一直这样做,但如果你搜索我的代码为Sass
的@while
,你会找不到它。 这是因为你可以用递归函数实现同样的事情,使其可重用:
展位图
现在我们可以在代码中的任何地方调用color()
函数。
Stylus没有while
循环的语法,但它也允许数组变量和递归函数:
展位图
Less没有内置的数组变量,但我们可以通过创建一个对列表来模仿相同的效果,就像我们对社交媒体颜色所做的一样:
展位图
我们必须创建自己的 @ array-get mixin
来使用键名从数组中检索值,然后创建我们的递归while
循环以遵循以下路径:
展位图
这适用于演示的目的,但在Less也许有一个更好的方法来做这个,因为你可以使用别名和命名空间变量而不使用数组(不像Sass或Stylus):
展位图
现在颜色可以用一个变量去成功实现,我可以使用另一个循环来生成我的调色板。 这里有一个快速示例Sass:
展位图
我相信你可以比我做的更好。
总结
如果你不确定什么时候在代码中使用循环,请留意重复。 你有多个选择器遵循类似的模式,或一个计算你一直在做? 以下是如何判断哪个循环是最好的:
如果你可以列出和命名循环中的项目,使用for-each
循环遍历它们。
如果重复次数比任何源项目集更重要,或者如果您需要您的项目编号,请使用for
循环。
如果您需要访问具有不同输入的相同循环,请尝试使用递归函数。
对于任何其他(几乎从不),使用while
循环。
如果你使用较少...祝你好运!
Have fun looping!