CSS布局在前端开发中像呼吸一样——再平常不过的事。比如同事A在尝到了Flexbox布局的甜头之后,任何布局都会以display:flex
打头阵,同事B因为项目得支持IE10,像避开毒蛇一样的避开Flexbox布局方法。你会发现我可能有点嗤笑这样的行为,我曾经也是这样的一员,我想为这个问题——当遇到css布局,你在考虑什么? 整理一个完整的解决方案。
你在考虑什么:从什么样的HTML结构出发能够帮助到css布局?你的布局方法武器库都有什么,在具体场景下,选择什么合适的布局方法?需要做支持旧浏览器吗?Flexbox、Grid这些布局的方法弄潮儿在旧浏览器中的最佳实践是?等等等等。
本文将介绍我的"答案",欢迎胖友们补充、更正。
Normal flow:css布局的起点
Normal flow(不知什么中文翻译妥帖,还是扔了英文...)指的是如果没有改变css布局代码,网页中标签的默认表现方式。比如:块级标签p
挨个从上往下,而内联标签span
表现得像段落中的文本。
当我们创建、自定义一个布局,其实是调整标签在Normal flow中的位置,或是直接从Normal flow移除,我们最最原始的材料就是Normal flow。如果使用语义化标签(semantic markup),从一个结构良好的HTML文档开始是很有帮助的:
- 语义化标签确保**内容可读,**即使是非常受限制的浏览器、像屏幕阅读器这样的设备也如此;
- 以此为起点去布局文档,是合作友好的,而不是破坏性的、改动很大的,因为大多数标签还是在Normal flow中;
HTML5新加了些帮助结构化的标签, 这篇文章可以参考,这里做一个overview:
header
:body
、main
标签的直接子标签,位置在页面头部,内容可能为logo、标语、搜索提示、导航栏;nav
:导航栏包在nav
标签内,可能出现在头部、侧边栏、底部等等,这里有个,神奇的地方在于设置nav
标签的display:inline-block
,是作用在li
标签上的;main
:body
标签的直接子标签,主内容区域;aside
:侧边栏;article
:一般出现在main
标签内,article
标签内可以有section
、footer
等标签,是比较独立的内容,比如像博客网站主页的一个文章简介;section
:section
和div
很类似,如果使用div
标签是为了对内容做样式控制,或者为了便于javascript获取做其他操作,那么使用div
就是你的答案,其他情况就用section
;address
:提供联系信息,放在article
标签内提供文章作者信息,放在main
、body
、footer
内提供网站信息;footer
:一般在HTML结构底部,补充网站信息,如果放在article
内补充文章信息;
Normal flow是CSS布局的起点,更好的选择是语义化标签(semantic markup)作为CSS布局的起点。
在具体场景下选择合适的布局方法
css布局方法有很多,如Flexbox、Grid、Float等等等等,在使用之前得把握两个中心思想:
- 每种布局方法有它的使用场景、使用上下文,在具体场景中选择对应合适的布局方法才是王道;
- 一个页面往往会应用多种布局方法,而不是一种布局方法解决所有问题,布局方法间是合作的关系;
接下来主要以讲demo的形式介绍各个布局方法的使用场景,对于布局方法自身如何使用不会过多说明。
Flexbox
Flexbox是Flexible Box Layout的简称,Flexbox既可以用于整个页面的布局,也可以用于局部部件的布局。Flexbox存在些浏览器兼容性的问题,在旧浏览器中的实践会在之后说明。接下来几个场景是建立在浏览器支持Flexbox的前提下。
Flexing sizing of flex items
Flexbox全称Flexible Box Layout中的Flexible(灵活性),是它的立命之本。Flexbox的第一个使用场景也呼之欲出——Flexing sizing of flex items,也就是盒子尺寸的高度灵活性:
- :
section
标签是Flex容器,article
标签是Flex item,其中前面两个article
标签flex:1 200px
,最后一个article
标签flex:2 200px
。具体表现为,如果不能提供3个Flex item都是200px
宽度的空间,则它们仨宽度一致,如果能提供,剩余空间按照1:1:2分配;
- :这是实际使用中一个很常见的做法,这里将
footer
标签高度固定,section
标签因为flex:1
而占据余下所有空间。在水平方向,也可以是侧边栏宽度固定,主要内容占据余下所有空间;
水平、垂直位置调整
Flexbox提供像align-items
、justify-content
这样的属性去调整flex items在主轴(main axis)、副轴(cross axis)的位置。比如最常见的考试题,水平垂直居中某个元素, ;再比如justify-content:space-around
作用于导航条的样式,。
调整标签顺序
一般来说,标签出现顺序由源代码中出现顺序决定,Flexbox为Flex items提供了order
属性,提供从css角度调整Flex items在页面中出现的顺序的能力。
补充一个黑科技
如果为Flex item设置主轴方向(main axis)的margin
值为auto
,比如主轴是横向的,设置margin-left:auto
,这个Flex item会占据往左这个方向的剩余空间:。
Grid
Grid布局,和Flexbox设计为在一个方向布局不同,它帮助我们更加容易地从两个方向上布局元素。我更加推荐Grid布局应用于整个页面,因为它非常清爽、优雅。它同样存在浏览器兼容问题,且比Flexbox更要重,在旧浏览器中的实践会在之后说明。接下来几个场景是建立在浏览器支持Grid的前提下。
优雅的整个页面布局
为什么说它优雅呢?看几个demo就知道了。
、:两个demo都实现了最基本的一个页面情况,一个头部、一个侧边栏、一个主要内容区域、一个底部,前者是Grid布局最常规的使用,后者使用了grid-template-areas
属性;
另外在Grid布局之前,有一些库在做模拟Grid System的工作,将一个页面分成6列或者12列,标签按列去占据页面。Grid布局方法完全有这样一个能力,使用12列布局的Grid重写前面两个demo实现的效果:;
如果能使用Grid布局整个页面,我是强烈推荐的,它的思维切入点不再是一维,而是二维,这是一场变革。
Floats
Floats布局方法既可以针对整个页面,也可以针对局部部件,虽然设计之初并不是为了布局整个页面。我是把Floats作为无法使用Grid、Flexbox时候的第一选择。像前面提到的做Grid System的css库,它其实也是将其中的每一个item设置为了float:left
,然后计算占据宽度的百分比以模拟Grid System。
另外,"floated item"(设置float:left
或float:right
)会从Normal flow中移除。来看看具体应用的demo吧。
文字环绕图片
“文字环绕图片”是Floats设计的初衷:
文本首单词首字母特殊处理
页面布局:一个最常见Floats问题的解决
"Floated item"的高度是不包括在容器标签内,如果高度超出容器标签,会出现显示上的错误,这是Floats应用于页面布局最常见的一个问题:
解决方案有三种:
- :在容器标签伪类
::after
清除浮动,或者在容器标签内加一个空的div
元素清除浮动也可以解决问题; - :使用
overflow
属性建立一个BFC,但是小心overflow:hidden
、overflow:auto
可能增加了你不需要的显示效果; - :更现代的方法是使用
display:flow-root
建立一个BFC,而且不会像overflow
增加不需要的显示效果,但是得考虑浏览器支不支持这个属性;
Table layout
在许多年以前,web开发者使用table
标签做整个页面的布局,将页面内容放入table
的行和列中,这种方法的问题在于不灵活,而且语义错误(对于屏幕阅读器的用户很不友好)。之所以放入table
标签能布局,是因为存在描述table layout的一些列css属性,它们是和table
这些标签是绑定的。而直接使用这些css属性,用于不是table
这些元素布局,这种方法被称为是 "using CSS tables" :;
"using css tables" 被称作是一种遗留方法(legacy method),用于整个页面布局,适用于不支持Flexbox和Grid的浏览器,但是我这里的最佳替补还是Floats。
Positioning
Positioning的定位和前面四种不太一样,它一般不用于创建整个页面布局,而是管理和微调标签,做一个局部位置的调整。要注意如果已经设置以下几个position
属性值的标签,层级是高于Normal flow,层级可通过z-index
属性调整。
position:relative
相对定位,做位置调整
:这个例子不是很深动形象,但是demo糙理不糙,确实是通过设置left
、top
等属性值去移动位置。
posision:absolute
绝对定位,做任何可弹出、可拖拽UI部件
MDN上放了这样一个使用场景说明:
popup information boxes and control menus; rollover panels; UI features that can be dragged and dropped anywhere on the page; and so on...
postion:fixed
固定定位
:固定表头,表头位置始终定于页面顶部,不随滚动条滚动而滚动。
当然可用于任何需要固定于页面某个位置的UI部件。
position:sticky
粘性定位
这里有个很经典的例子: ;但是在使用时得考虑浏览器兼容问题,兼容性目前堪忧。
Multicol
Multicol是Multi-columns layout的简称,它提供了一种在列中布置内容的方法,类似于文本在报纸中的流动方式,使得阅读更加友好,不用上下滚动。Multicol的定位是这一种特殊的内容展示布局。
报纸阅读模式
:通过在container
块级元素上设置column-count
或者column-width
属性开启Multicol:
Flexbox、Grid考虑支持旧浏览的最佳实践
最初吸引我做这个话题的原因,是目前公司项目得支持IE10、IE11,现状是项目中的布局方法没有Grid、鲜有Flexbox,就比较心痒痒,想搞搞明白到底能不能在支持IE10、IE11的情况使用这两种潮流的布局方法。所以在旧浏览器中的实践重点考虑的是IE10、IE11两位。
Flexbox: Postcss插件Autoprefixer
浏览器对Flexbox的支持还是挺不错的,IE10支持2012版语法,IE11支持的语法和现代浏览器一毛一样。在IE10和IE11中使用Flexbox存在一些已知的问题,在有说明,同时还有一个是一个问题的列表以及解决措施。
所以这里的最佳实践分两步:
- 借助Postcss插件为我们自动加上前缀,以支持IE10的2012版语法和现代语法;
- 使用过程避开在IE10和IE11中使用Flexbox的已知问题,如果还是碰到了在旧浏览器和现代浏览器中表现不一致,去 找找有没有相同情况。如果再没有,再考虑替换方案,也可以给 Flexbugs 这个项目提issue;
另外贴两篇Postcss扫盲文章:、
Grid: Feature Queries
浏览器对Grid的支持较Flexbox要差很多,IE10、IE11支持的是旧版本的规范,是带有-ms-
前缀,但即使使用autoprefixer补上了前缀,相同属性名相同属性值在页面中的表现也可能不一致。这样我是不推荐Flexbox实践中的方法,而是使用Feature Queries。
Feature Queries是使用css的@supports
,@supports
用于检测浏览器是否支持参数中的属性属性值,如果支持则渲染花括号中的css代码,类似于:
@supports (display: grid) { // code that will only run if CSS Grid is supported by the browser }复制代码
这里有个细节点,IE10、IE11是不支持@supports
规则,所以压根不会进入这个条件判断,花括号中的css代码是不会渲染的,这与我们考虑的逻辑:支持@supports
规则、不支持display:grid
是不同的,但是最后的结果是一样的。
以一个例子讲述一下整个流程:
- 首先是给旧浏览器做支持,准备一套Fallback method,保证在所有浏览器上都是工作的:
.wrapper{ overflow:auto;}.item { float:left; width:33.3%;}复制代码
- 再给支持Grid的浏览器做覆盖,覆盖代码分两部分,一部分是直接放入对旧浏览没有影响的:
.wrapper { display: grid; grid-template-columns: 1fr 1fr 1fr;}复制代码
因为旧浏览器不支持Grid布局,Grid相关属性旧浏览器都无法解释。在支持的浏览器中使得item由floated item转为grid item,这样的覆盖行为由css规定,更多覆盖情况见。另一部分是直接放入对旧浏览器是有影响的,要做Feature queries:
@supports (display: grid) { .item { width: auto; }}复制代码
覆盖原有的width:33.3%
。
没错,这里的实践得写两套样式。所以有人提出问题,写一套支持所有浏览器的不就得了,干嘛非得用Grid?这是个很实际的问题,毕竟写两套,再加测试调试,会增加一定工作量。有几个场景建议使用Grid:
- 项目得支持IE10、IE11等旧浏览器,但是开发者想尝鲜Grid布局,Feature Queries提供了这样的能力;
- 项目周期会很长,可能现在不支持Grid布局的浏览器,以后就支持了;
- 要实现的效果不使用Grid布局很难实现,且对在旧浏览器中访问效果要求不高,能看就行;
测试
尤其是支持IE10、IE11的项目,测试是很重要的一个环节,最佳的测试还是在各个浏览器中打开。但这里存在获取浏览器的问题,例如win10系统上仅有IE11,而不能使用IE10等。有些公司有自己的服务器,有各种浏览器可供测试;如果没有的话,可以考虑下载虚拟机: ,或者使用像 访问远程的虚拟机。
从开发者角度,整个工作流程应该是这样子:
- 初始开发计划制定
- 开发
- 测试、发现问题
- 修复问题,重复2~4步骤
总结
- 做css布局
- 布局的出发点是语义化标签
- 考虑在具体场景下使用什么布局方法最合适最简单
- 考虑要不要支持旧浏览器,要明确支持不意味着显示一模一样,可存在体验优秀+体验一般两种模式
- Flexbox、Grid考虑旧浏览器的实践(支持IE10、IE11)
- Flexbox支持性比Grid好,使用Autoprefixer前缀,避开Flexbox bug、已知issues,放开了使用
- Grid布局要想使用,得用Feature Queries的方法,额外准备一套Fallback Methods
- Autoprefixer关闭对Grid属性添加前缀(默认行为)
- 测试
- 测试流程:初始开发计划制定 > 开发 > 测试、发现问题 > 修复问题,重复2~4步骤
- 借助虚拟机等