为了简单实用, 布局就直接从flex开始讲起. 要想让block或flex元素在一行展示, 可以在父元素设置属性display: flex, 或者子元素设置inline-flex.
先看第一种方式, 由于flex-direction的默认值是row, 而flex-wrap默认是nowrap, 也就是flex元素的子元素默认会在一行显示, 不会自动换行. 我感觉遇到这种个数不确定而且自动平分空间的场景比较少, 多数情况下列表是子元素固定宽度, 并且按宽度自动换行的, 所以我把flex元素都设置了flex-wrap: wrap. flex容器对于子元素的控制主要是通过 justify-content 和 align-items 两个属性, 这两个属性的默认值是normal,就相当于水平位置从起始位置开始排列(flex-start), 竖直方向拉伸至填满父元素高度(stretch). 由于多数场景下使用flex都是为了居中, 所以我把这两个属性都设置为center, 如果又需要对齐其他方向的再单独设置.
为方便描述, 以下把这两个属性简称为水平垂直属性.
首先看一种简单的情况, 一个display: flex的div, 里面有一段文字内容. 在水平垂直两个属性都设置为center时, 文字内容会在容器中水平垂直居中. 即使文字有多行依然表现良好. 所以flex极大的方便了页面布局, 在没有flex的年代, 需要通过line-height, vertical-align等属性复杂的计算和组合实现文字垂直居中, 而且不一定能很好的适应各种浏览器环境. 如果需要其他文字对齐方式, 正常调整水平垂直属性即可.
然后是内部包含子元素的情况, 其实也是类似的, 由父元素的水平垂直属性决定排列方式. 不同之处在于, 子元素可以设置尺寸, 此时元素的排列是根据父元素的水平垂直属性, 以及子元素的宽高和margin共同决定. 水平垂直属性也提供了几种常见的排列方式方便使用, 如space-around, space-between, space-evenly等, 不过实际场景中我们遇到的可能不是这些固定的排列方式, 所以就需要通过margin来调整. 简单的说, margin: auto会让子元素平分可用空间, 而单侧的margin-left: auto会让元素居右显示. 多数场景下使用这两个值都足够了, 可以代替float让元素右浮动的方法.
最后是一种更常见的场景, 使用flex排版列表, 其实关键是flex-wrap: wrap实现在容器边缘自动换行, 其他间距可以通过水平垂直margin调整. 也就是说, 通过在flex容器中使用margin, 即使不理解复杂的flex概念, 也可以实现布局. 个人认为是一种比较经济的方式.
当然, 如果能够深入学习和理解一下flex的概念, 对于日后学习grid布局也是有好处的, 很多概念是类似的. 其实难理解的主要是两个方面, 一是规范为了理论的严谨性, 使用了主轴和交叉轴的表达方式, 其实我们就简单理解成水平垂直属性就好了, 多数情况下使用flex都是为了水平排列元素, 如果确实遇到了少数场景要设置flex-direction为其他值, 可以先按水平排列的情况思考, 写好之后再把水平垂直属性互换一下. 另一方面是规范中大量的属性和篇幅在讲剩余空间的分配问题, 由于我们直接使用margin自己控制间距分配, 所以也避免了和复杂的分配规则打交道, 而且多数时候, 设计和业务想要的间距都不会按照规范制定者所想的方式, 基本都需要逐个手动设置.
然后说一下inline-flex, 其实就是让外部元素能和其他元素在同一行, 内部空间管理都是一样的. 之前还有inline-block属性, 也可以实现很多布局, 但是由于依赖inline模型的很多概念, 复杂难用, 所以现在基本都被flex取代了.
其实从历史和完整性的角度, 还是应该介绍一下float, 但是限于时间和篇幅, 以后如果有时间再补充吧.
定位
定位其实就是一个属性position, 值也不多, 默认值static, 然后是 relative | absolute | fixed, 以及一个不太稳定的sticky. 其中我们主要关注的是absolute.
如果一个元素设置了position: absolute, 就会有一些布局上的变化, 按照术语, 首先是绝对定位元素脱离标准文档流, 这个需要一些基础概念来辅助理解, 我们可以先理解成创建了一个单独的层, 和原先最下面的平面布局层相对独立. 如果试验一下就会发现, 自身的位置可能不会变化, 但是之后的元素会跑到他的下面. 这种状态也被称为'无依赖绝对定位', 因为设置位置的top/right/bottom/left四个方位值默认都是auto, 按照CSS规范, 元素的位置应该是在原先自己所处的位置. 但是层级会提高, 盖在其他元素之上. 此时元素会块状化, 但是和block形成的块不太一样, 不会自动填充宽度, 需要手动设置宽高或者根据内容决定尺寸. 要设置元素的位置可以通过两种方式, 一种是用四个方向上的定位属性, 但是定位的依据是包含块, 没有设置的话默认是根元素, 所以top: 0会把元素定位到页面顶部. 如果想改变包含块, 需要找一个上级元素设置上position属性, 因为绝对定位元素的包含块计算方式是: 最近的非 static 定位祖先元素. 另一种方式是通过margin控制, 由于margin支持负值, 也就可以在四个方向上移动, 这样不需要更改父元素的定位方式, 但是可能无法实现精确定位到距离右边或底边的指定位置.
实际场景中, 绝对定位一般用于将某个元素显示在固定位置. 所以通常会设置父元素为相对定位. 如果确实需要相对整个页面定位的, 可以用position: fixed, 概念和用法都是类似的, 只是强制指定了包含块为viewport也就是浏览器窗口, 注意这和默认的包含块为根元素也还是有一些区别, 因为页面滚动的时候, fixed元素是不会跟着动的, 而absolute元素会跟着自己的包含块移动. 所以说, fixed是一种特殊的绝对定位.
而relative多数情况下是为了给绝对定位元素设置包含块用的. 一个元素设置position: relative之后, 自身位置不会变化, 也不会脱离标准文档流, 不像absolute那样对其他元素有影响. relative的设计原意是让元素相对于自身原有位置偏移, 但是这一特性在实际场景中使用较少, 多数情况下都是使用margin进行偏移, 但是有一些区别在于, relative通过定位方式偏移之后, 自身原有的位置仍然保留, 不会影响其他元素, 可以理解为是在单独的一层上偏移的, 而margin偏移和文档流中元素是在同层的, 偏移之后其他元素的位置也会跟随变化. 所以这也是relative的一个应用场景, 提高元素的层级, 之后我们会看到, 设置了position属性的元素会创建新的层叠上下文.
所以在实际页面布局时, relative通常都是配合absolute使用, 此时relative元素作为absolute元素的包含块, 偏移位置都是相对包含块计算, 所以通过right和bottom偏移可以让元素定位在包含块的右边或底边指定位置. 另外元素的宽高百分比值也会相对于包含块计算. 另外一个特性是垂直方向上margin: auto会让元素在包含块中垂直居中. 不过在用flex布局之后这个特性的使用机会也不多了.
另外一个值sticky, 比较新, 兼容性也不好保证, 他的特点简单理解就是根据元素的位置是否在浏览器viewport显示范围内, 在relative和fixed之间切换. 有些场景下是有用的, 但是由于兼容性问题, 用之前需要查一下浏览器支持情况, 还要在真机上做测试, 我们就不多关注了.