对很多人来说,创建布局是前端开发领域中最难啃的骨头之一。
你肯定经历过耗费数个小时,换着花样地尝试所有可能起作用的CSS属性、一遍遍地从StackOverflow上复制粘贴代码,寄希望于误打误撞地赌中那个能实现预期效果的魔幻组合。
如果你的惯用策略就是按部就班地组合布局——先把A元素放在这儿,好了,A元素就位了,我再看怎么把B放在那儿……那你没有挫败感才怪呢。CSS的玩法可与SKetch或者Photoshop的玩法不一样。
在本文中,我将向你展示如何以统筹全局的思维实现CSS布局,根治布局难产的顽疾。
我们将用一个小案例贯穿全文,我会把所有的CSS代码都解释给你听,因此即使你不知道或者忘记了position和display的用法,即使你分不清align-items和justify-content的区别,你仍会有所斩获。
而且我们会用纯HTML和CSS代码来演示,因此你不需要React、Vue、Angular、CSS-in-JS甚至是JavaScript方面的知识储备。
听起来很棒吧?那就开始吧。
布局小例子
在本文中,我们要比照Twitter的推文组件自己仿写一个:
不论是一个像这样的草图,还是一个细节精美的原型图,“有章可循”总是个好主意。
要避免一边在脑海里设计,一边在浏览器中七拼八凑地攒布局,这样的开发过程才会更顺畅。你当然可以达到那种手脑合一的境界!但鉴于你还在乖乖地读这篇文章,我可以假设你还没有那么神通广大。?
第一步:分而治之
在动手敲代码之前,我们先把布局的各个单元区分开来:
在用CSS铺排布局时,用行和列的形式去构思大有裨益。因此,要么你把元素从上到下排列,要么从左到右排列。这种行和列的思路完美对应了CSS中两种布局技术:Flexbox和Grid。
当然了,我们的示例布局并不是中规中矩的行列。它有一张图片镶嵌在左侧,其他元素排列在右侧。
第二步:沿着各个单元画方框
画一些方框把这些元素框起来,看看行和列是否初具规模。我们把方向一致的单元归到同一个方框中。
在页面中的HTML元素基本上都可视为矩形。当然,有些元素有圆角,有些元素是圆形,或者是复杂的SVG形状等。通常你看不到页面上有一堆矩形。但你可以用矩形边框的模式去分析它们。这样的想象能帮你理解布局。
之所以提到矩形,是因为你要把一系列元素对齐——如第一行的用户名、@handle(译者注:handle属于专有名词,指Twitter中的用户ID,所以在本文中保留不译。详见www.urbandictionary.com/define.php?…)和时间以及最后一行的图标——把它们用方框包起来便于规划。
按目前的规划,把布局用HTML代码实现出来大概如下所示:
<img
src=“http://www.gravatar.com/avatar”
alt=“Name”
/>
@handle
Name
3hago
Someinsightfulmessage.
- Reply
- Retweet
- Like
- …
-
展示出的效果是这样的(可以点击这里调试代码):
这离我们想要的效果还远呢。但是!所有所需的内容都齐全了。有些元素还以从左到右的顺序排列。
我们可以认为,即使不用进一步设置样式,目前的布局效果也能达到网页想表达的要点,这也是一个优秀的HTML应该达到检查标准。
关于语义化HTML的说明
你可能会好奇,为何我选的是那些元素——article、p等等。为何不都用div呢?
为何要这样写:
<img…/>
…
-
…
而不这样写?
<img…/>
……
其实,每个HTML元素的名称都有其特定含义,在不同场景中恰如其分地使用语义上与它们所表示的内容匹配的元素,是很好的语义化实践。
这种写法,首先,有助于开发者理解代码;其次,对使用屏幕阅读器等辅助设备的用户比较友好。同时这样用标签也有利于SEO——搜索引擎会试着理解这个页面的含义,以便于显示相关广告来盈利、帮助搜索者找到满意结果。
article标签代表文章类内容,而你可以认为推文这种东西有点类似于一篇文章。
p标签代表段落,而推文的内容文本有点类似于一个段落。
ul标签代表无序列表(与有序列表或数字序号列表相对应),在本示例中,你可以用它来存放列表信息。
我们无法用只言片语就说清楚HTML元素的语义,以及何种情况用何种标签。但大多数情况下,一个语义化元素即使其语义再不贴切,也比用div强,div标签只代表“一块区域”。
元素的默认样式
是什么决定了元素的样式?为什么有的元素独占一行,而有的元素能共处一行?
这要归因于元素的默认样式,这其中就有我们要探讨的第一个CSS知识点:行内元素和块级元素。
行内元素们肩并肩挤在一行里(就像句子中的词一样,必要时会折行)。根据再浏览器中的默认样式划分,span、button以及img都是行内元素。
而块级元素,总是踽踽独行。以控制台输出的方式去理解,你可以认为块级元素前后各有一个换行符\n。就好像console.log(“\ndiv\n”)。article、div、li、ul以及p标签都是块级元素。
注意,在上面的例子中,为什么即使img标签是行内元素,头像图片依然独占一行?因为它下方的div是块级元素。
然后要注意,为什么@handle、用户名和时间都在同一行?原因是它们都在span标签中,而span是行内元素。
这三个span和文字“insightfulmessage”处于不同行,因为(a)它们被包在一个div中,div后面自然要另起一行;(b)p标签同样是块级元素,它自然从新行开始排列。(之所有没有出现两个空行,是因为HTML合并了相邻的空行,与相邻空格同理。)
如果你再看得仔细点,你会发现“insightfulmessage”的上下方空间,要比头像图片以及handle、用户名、时间的上下方空间要大。此空间的大小也由默认样式控制:p标签的顶部和底部都有margin。
你也会注意到按钮列表的圆点,以及列表的缩进行为。这些也都是默认样式。我们马上就要修改这些默认样式了。
第三步:再画一些方框
我们想把头像图片放在左侧,其余元素放在右侧。你可能会根据刚刚探讨的行内和块级知识来推断,认为只要把右侧的元素都包裹到一个如span标签般的行内元素中,就完事大吉了。
但这是行不通的。行内元素并不能阻止其内部的块级元素另起一行。
为了把这些元素收拾得服服帖帖,我们需要用一些更强大的技术,比如Flexbox或者Grid布局。这次我们选用Flexbox来解决。
Flexbox的原理
CSS的Flex布局能够把元素以行或者列的形式排布。这是一种单向的布局系统。为了实现交叉的行和列(正如推文组件的设计那样),我们需要添加一些容器元素来扭转方向。