《深入解析css》 |精通布局
前言:所有内容与示例源码源于基思·J·格兰特的《深入解析css》,文章用于笔记整理。源码仓库
一、浮动布局
引入
浮动元素会被移出正常文档流,文档流会重新排列并包围浮动元素。这种布局在报纸和杂志中很常见。
容器折叠
浮动元素的高度不会加到父元素上。所以当子元素的高度比父元素大时,父元素不会撑开。解决办法:使用clear属性。将一个元素放在主容器的末尾,并对它使用clear,这会让容器扩展到浮动元素下面
<style>
.container{
max-width:1080px;
margin:0 auto
}
.children{
float:left;
}
</style>
<div class=container>
<h1>标题</h1>
<div class=children></div>
<div class=children></div>
...
<div style="clear:both"></div>
</div>
双容器模式居中法:
清除浮动
-
在父元素里的末尾加上一个空标签(如上)
-
用伪元素清除浮动
.clearfix::after{ display:block; content:" "; clear:both }
上方的写法不能解决外边距折叠问题,下面是修改后的方案:
-
伪元素清除浮动修改版
.clearfix:before,.clearfix:after{ content:''; display:table; } .clearfix:after{ clear:both; }
-
给父级容器标签加上overflow属性(非visible)
.container{ overflow:hidden|auto|scroll }
浮动陷阱
浏览器会将浮动元素尽可能地放在靠上的地方,所以有时候会发生这种情况:
解决办法:清除每行的第一个元素上面的浮动。假设你一行需要放两个浮动元素,那么你可以这样写:
.box:nth-child(odd){
clear:left
}
如果每行需要三个元素:
.box:nth-child(3n+1){
clear:left
}
上面这种清除每行浮动的技术要求知道每行有几个元素。如果宽度不是通过百分比来定义的,最好使用别的布局方案,比如Flexbox或者inline-block元素。
媒体对象和BFC
图片在一侧,文字在图片旁边。Web开发人员Nicole Sullivan把这种布局称作“媒体对象”。这种布局模式有好几种实现方案,包括Flexbox和表格布局,但这里我们用浮动。
<div class="media">
<img class="media-image"src="shoes. png">
<div class="media-body">
<h4>Change it up</h4>
<p>
Don't run the same every time you hit the road.
Vary your pace, and vary the distance of your
runs.
</p>
</div>
</div>
.media-image{
float:left
}
.media-body h4{
margin-top:0
}
上面的方式就是给正文建立一个块级格式化上下文(block formatting context, BFC)。BFC是网页的一块区域,元素基于这块区域布局。创建BFC的元素做出了以下3件事情:
- 包含了内部所有元素的上下外边距。它们不会跟BFC外面的元素产生外边距折叠
- 包含了内部所有的浮动元素
- 不会跟BFC外面的浮动元素重叠
创建BFC的方式:
- float: left或right,不为none即可
- overflow:hidden、auto或scroll,不为visible即可
- display:inline-block、table-cell、table-caption、flex、inline-flex、grid或inline-grid。拥有这些属性的元素称为块级容器(block container)
- position:absolute或position: fixed。说明网页的根元素也创建了一个顶级的BFC。
网格系统
浮动布局无法轻松地复用样式表中的内容。比如现在媒体对象的宽度是50%,因此一行有两个元素。如果想要复用前面的设计,但需要一行放三个元素,那又该怎么办呢?一种比较普遍的做法是借助网格系统提高代码的可复用性。网格系统提供了一系列的类名,可添加到标记中,将网页的一部分构造成行和列。它只给容器设置宽度和定位。
大部分流行的CSS框架包含了自己的网格系统。接下来构建一个网格系统,这样你就能掌握它的工作原理,进而应用到网页中。
CSS框架:一个预编译的CSS代码库,提供了Web开发中常见的样式模式。它能够帮助快速搭建原型或者提供一个稳定的样式基础,辅助构建新样式。常见的框架包括Bootstrap、Foundation以及Pure。
理解网格系统
要构建一个网格系统,首先要定义它的行为。通常网格系统的每行被划分为特定数量的列,一般是12个,但也可以是其他数。每行子元素的宽度可能等于1~12个列的宽度。下面代码里的标记直观地展示了网格系统:
<div class="row">
<div class="column-4">4 column</div>
<div class="column-8">8 column</div>
</div>
构建网格系统
首先,行元素主要是给列元素提供一个容器,并将其包裹起来。清除浮动恰好能起到这个作用:
.row::after{
content:" ";
display:block;
clear:both
}
其次,给列元素添加初始样式。只需将所有的列都浮动到左边,并给每种列元素指定宽度值。可能需要花点时间计算出不同列的宽度百分比,要精确到小数点后几位,以免因为四舍五入而导致误差。
[class*="column-"] {float: left;} //属性选择器,匹配包含column-的元素
.column-1 { width: 8.3333%; } // 1/12
.column-2 { width: 16.6667%; } // 2/12
.column-3 { width: 25%; } // 3/12等
.column-4 { width: 33.3333%; }
.column-5 { width: 41.6667%; }
.column-6 { width: 50%; }
.column-7 { width: 58.3333%; }
.column-8 { width: 66.6667%; }
.column-9 { width: 75%; }
.column-10 { width: 83.3333%; }
.column-11 { width: 91.6667% }
.column-12 { width: 100%; }
添加间隔
给每个网格列添加左右内边距,创造间隔。因为需要列之间有1.5em的间隔,所以可以将其分成两半,给每个列元素左右各添加一半的内边距
[class*="column-"] {
float: left;
padding:0 0.75em; //添加间隔
margin-top:0 //去除顶部外边距
}
调整:去掉每行第一列的左侧内边距和最后一列的右侧内边距
.row{
margin-left:-0.75em;
margin-right:-0.75em;
}
总结
- 浮动的设计初衷是让文字围绕一个元素排列
- 使用清除浮动来包含浮动元素
- BFC有3个好处:包含浮动元素,防止外边距折叠,防止文档流围绕浮动元素排列
- 使用双容器模式让页面内容居中
- 使用网格系统实现更丰富的网页布局。
二、弹性布局
Flexbox,全称弹性盒子布局(Flexible Box Layout)跟浮动布局相比,Flexbox的可预测性更好,还能提供更精细的控制。它也能轻松解决困扰我们许久的垂直居中和等高列问题。
原则
给元素添加display: flex,该元素变成了一个弹性容器,它的直接子元素变成了弹性子元素。下面是三个概念的一些说明:弹性容器、弹性子元素、两条轴:
- 弹性子元素默认是在同一行按照从左到右的顺序并排排列
- 弹性容器像块元素一样填满可用宽度
- 弹性子元素不一定填满其弹性容器的宽度
- 弹性子元素高度相等,该高度由它们的内容决定
- 弹性容器能控制内部元素的布局
- 子元素按照主轴线排列,主轴的方向从左到右。副轴的方向从上到下。可改变
示例
结构
<div class="container">
<!-- 标题 -->
<header>
<h1>Ink</h1>
</header>
<!-- 导航菜单 -->
<nav>
<ul class="site-nav">
<li><a href="/">Home</a></li>
<li><a href="/features">Features</a></li>
<li><a href="/pricing">Pricing</a></li>
<li><a href="/support">Support</a></li>
<li class="nav-right"><a href="/about">About</a></li>
</ul>
</nav>
<!-- 主体版块 -->
<main class="flex">
<!-- 左侧容器 -->
<div class="column-main tile">
<h1>Team collaboration done right</h1>
<p>Thousands of teams from all over the
world turn to <b>Ink</b> to communicate
and get things done.</p>
</div>
<!-- 右侧容器 -->
<div class="column-sidebar">
<!-- 1.登陆容器 -->
<div class="tile">
<form class="login-form">
<h3>Login</h3>
<p>
<label for="username">Username</label>
<input id="username" type="text" name="username"/>
</p>
<p>
<label for="password">Password</label>
<input id="password" type="password" name="password"/>
</p>
<button type="submit">Login</button>
</form>
</div>
<!-- 2.价格容器 -->
<div class="tile centered">
<small>Starting at</small>
<div class="cost">
<span class="cost-currency">$</span>
<span class="cost-dollars">20</span>
<span class="cost-cents">.00</span>
</div>
<a class="cta-button" href="/pricing">
Sign up
</a>
</div>
</div>
</main>
</div>
基础样式
/* 全局设置box-sizing */
:root{
box-sizing: border-box;
}
*,::before,::after{
box-sizing: inherit;
}
/* 设置页面的背景颜色和字体 */
body{
background-color: #709b90;
font-family: Arial, Helvetica, sans-serif;
}
/* 全局设置外边距 */
body *+*{
margin-top: 1.5em;
}
/* 双容器实现居中 */
.container{
max-width: 1080px;
margin: 0 auto;
}
导航菜单
要实现这个菜单,需要考虑让哪个元素做弹性容器。在本例中,弹性容器应该是<ul>
,它的子元素<li>
.site-nav{
/*1.给容器加上弹性布局*/
display: flex;
background-color: #5f4b44;
/* 2.覆盖列表默认样式 */
list-style-type: none;
padding-left: 0;
}
.site-nav>li{
/* 3.覆盖猫头鹰顶部外边距 */
margin-top: 0;
}
.site-nav>li>a{
background-color: #cc6b5a;
color: white;
text-decoration: none;
}
旧版浏览器要求给Flexbox属性加上浏览器前缀。比如,旧版Safari浏览器没实现display:flex,而是实现了display:-webkit-flex。需要写成:
display:-ms-flexbox; //为了支持旧版IE display:-webkit-flex; //为了支持旧版Safari display:flex;
.site-nav{
...
/* padding-left: 0; */
/* 5.优化容器 给菜单容器加上内边距 */
padding: .5em;
border-radius: .2em;
}
.site-nav>li>a{
...
/* 6.优化连接 让链接变成块级元素,使之撑开父元素高度。并给链接加上内边距 */
display: block;
padding: .5em 1em;
}
/* 7.添加间隔 借鉴猫头鹰选择器。选中除第一个外所有li,为其添加左外边距 */
.site-nav>li+li{
margin-left: 1.5em;
}
/* 8.修改按钮位置 利用弹性盒子的特性,把外边距设置为auto,使其填充所有可用空间 */
.site-nav>.nav-right{
margin-left: auto;
}
flex设置大小
可以用width和height属性设置它们大小Flexbox提供了更多更强大的选项——flex。flex属性控制弹性子元素在主轴方向上的大小。在该例中给网页的主区域应用弹性布局,并使用flex属性控制每一列的大小。首先先完成基础样式部分:
/* 1.将主容器设置为弹性布局 */
.flex{
display: flex;
}
/* 2.给三个板块加上白色背景和内边距 */
.tile{
padding: 1.5em;
background-color: #fff;
}
/* 3.去掉右侧容器的顶部外边距,并给每个子元素加上间隔*/
.flex>*+*{
margin-top: 0;
margin-left: 1.5em;
}
目前还没有特别设置两列的宽度,所以是根据内容自适应的宽度。可见没有完全填满可用空间。本例用column-main和column-sidebar类来指定两列,现在使用flex属性给两列分别赋以2/3和1/3的宽度:
.column-main{
flex: 2;
}
.column-sidebar{
flex: 1;
}
flex属性是三个不同大小属性的简写:flex-grow、flex-shrink和flex-basis。你也可以分别声明三个属性。接下来看看三个属性分别表示什么:
flex-grow:1;
flex-shrink:1;
flex-basis:0%
flex-grow:2;
flex-shrink:1;
flex-basis:0%
-
flex-basis:定义了元素大小的基准值,即一个初始的“主尺寸”。可以设置为任意的width值,包括px、em、百分比,它的初始值是auto。
每个弹性子元素的flex-basis值计算出来后,它们(加上子元素之间的外边距)加起来会占据一定的宽度。加起来的宽度不一定正好填满弹性容器的宽度,可能会有留白,如下图:
每个弹性子元素的初始主尺寸确定后,它们可能需要在主轴方向扩大或者缩小来适应(或者填充)弹性容器的大小。对于上面的情况,可能需要flex-grow和flex-shrink来决定缩放的规则。
-
flex-grow:增长因子。多出来的留白会按照flex-grow的值分配给每个弹性子元素,值为非负整数。如果一个弹性子元素的flex-grow值为0,那么它的宽度不会超过flex-basis的值;如果某个弹性子元素的增长因子非0,那么这些元素会增长到所有的剩余空间被分配完,也就意味着弹性子元素会填满容器的宽度。
flex-grow的值越大,元素的“权重”越高,也就会占据更大的剩余宽度:
-
flex-shrink:防止溢出。计算出弹性子元素的初始主尺寸后,它们的累加值可能会超出弹性容器的可用宽度。如果不用flex-shrink,就会导致溢出
每个子元素的flex-shrink值代表了它是否应该收缩以防止溢出。如果某个子元素为flex-shrink: 0,则不会收缩;如果值大于0,则会收缩至不再溢出。按照flex-shrink值的比例,值越大的元素收缩得越多。
实际应用
弹性方向
弹性布局的另一个重要功能是能够切换主副轴方向,用弹性容器的flex-direction属性控制。如前面的例子,它的初始值是row
现在的布局有一个隐藏的缺陷,给主板块添加更多内容,会发现主板块超出了右边板块的底部。Flexbox应该能让两列的高度相等,为什么不起作用了呢?
其实左右两个弹性子元素是等高的。问题是右边栏内部的两个板块没有扩展到填满右边栏区域。如果想让两列扩展到填满容器的高度。要将右边栏(column-sidebar)改为弹性容器,并设置flex-direction: column
。然后给里面的两个板块设置非0的flex-grow值:
.column-sidebar{
flex: 1;
/* 1.对右侧容器设置弹性布局与弹性方向 此时它对外来说是弹性子元素,对内来说是弹性容器*/
display: flex;
flex-direction: column;
}
.column-sidebar>.tile{
/* 2.给右侧容器的子元素加上flex-grow */
flex: 1;
}
内部的弹性盒子的弹性方向为column,表示主轴发生了旋转,现在变成了从上到下。现在对于弹性子元素而言,flex-basis、flex-grow和flex-shrink现在作用于元素的高度而不是宽度。
说明:水平弹性盒子与垂直的弹性盒子有一点不同:水平弹性容器会占据100%的可用宽度,而高度则由自身的内容来决定。在垂直的弹性盒子里,子元素的flex-grow和flex-shrink不会起作用
完善登陆表单
/* 标题设置加粗、右对齐、全大写 */
.login-form h3{
margin: 0;
font-size: .9em;
font-weight: bold;
text-align: right;
text-transform: uppercase;
}
/* 给输入框添加样式 */
.login-form input{
display: block;
width: 100%;
margin-top: 0;
}
.login-form button{
margin-top: 1em;
/* 覆盖按钮默认样式 */
border: 1px solid#cc6b5a;
background-color: white;
padding: .5em 1em;
cursor: pointer;
}
弹性容器属性
flex-direction | 指定了主轴方向,副轴垂直于主轴 | |
---|---|---|
row | 主轴从左到右 | |
row-reverse | 主轴从右到左 | |
column | 主轴从上到下 | |
column-reverse | 主轴从下到上 | |
flex-wrap | 是否允许弹性子元素在弹性容器内折行/列显示 允许后,子元素不再根据flex-shrink值收缩 |
|
nowrap | 不允许 | |
wrap | 折行 | |
wrap-reverse | 折行且从末尾开始排列 | |
flex-flow | flex-direction和flex-wrap的简写 | |
justify-content | 控制子元素在主轴上的位置 任意子元素的flex-grow的值不为0, 或者任意子元素在主轴方向的外边距值为auto 该属性失效 |
|
flex-start | ![]() |
|
flex-end | ![]() |
|
center | ![]() |
|
space-between | ![]() |
|
space-around | ![]() |
|
align-items | 控制子元素在副轴上的位置 | |
flex-start | ![]() |
|
flex-end | ![]() |
|
center | ![]() |
|
stretch(初始) | ![]() |
|
baseline | ![]() |
|
align-content | 如果开启了flex-wrap,align-content就会控制 弹性子元素在副轴上的间距。如果子元素没有换行,会忽略该属性 |
|
flex-start | ![]() |
|
flex-end | ![]() |
|
center | ![]() |
|
stretch | ![]() |
|
space-between | ![]() |
|
space-around | ![]() |
弹性子元素的属性
flex-grow | 指定“增长因子”,填充未使用空间 | ![]() |
---|---|---|
flex-shrink | 指定“收缩因子”,防止溢出 如果弹性容器开启了flex-wrap,则会忽略该属性 |
![]() |
flex-basis | 指定子元素初始大小 | |
flex | flex-grow、flex-shrink、shrink的缩写 | |
align-self | 控制子元素在副轴上的对齐方式 会覆盖容器上的align-items值 若元素副轴方向上的外边距为auto,则忽略 |
|
auto | ![]() |
|
flex-start | ![]() |
|
flex-end | ![]() |
|
center | ![]() |
|
stretch | ![]() |
|
baseline | ![]() |
|
Order | 整数,将子元素从兄弟节点中移动到指定位置,覆盖源码顺序 |
完善示例
接下来使用以上介绍的一些属性来完成网页最后一个板块:
解析:文字\(20.00被包裹在`<div class="cost">`中。该元素将作为弹性容器,它有三个弹性子元素,放置三个需要对齐的文字部分:\)、20、.00
/* 给容器设置文本居中 */
.centered{
text-align: center;
}
/* 给文字容器设置弹性布局 */
/* 指定子元素在主轴和副轴上居中排列 */
.cost{
display: flex;
justify-content: center;
align-items: center;
line-height: .7;
}
/* 覆盖猫头鹰选择器的外边距 */
.cost>span{
margin-top: 0;
}
.cost-currency{
font-size: 2rem;
}
.cost-dollars{
font-size: 4rem;
}
/* 覆盖子元素的align-items,单独改变子元素排列方式,改成顶部对齐而不是垂直居中 */
.cost-cents{
font-size: 1.5rem;
align-self: flex-start;
}
.cta-button{
display: block;
background-color: #cc6b5a;
color: #fff;
padding: .5em 1em;
text-decoration: none;
}
三、网格布局
引入
CSS网格可以定义由行和列组成的二维布局,然后将元素放置到网格中。网格的大小既可以精确定义,也可以根据自身内容自动计算。可以将元素精确地放置到网格某个位置,也可以让其在网格内自动定位,填充划分好的区域。
与弹性布局对比,浏览器实现弹性布局的早期版本时,加浏览器前缀才能使用。随着规范的演变,浏览器更新了实现方式,开发人员也必须相应地更新自己的代码,但是还要将以前的代码保留以支持旧版的浏览器,这导致Flexbox问世的经历十分坎坷。与此同时,浏览器几乎可以完全实现网格布局。
构建基础网格
现在先创建一个简单的网格布局,以确认浏览器支持该特性。跟Flexbox类似,网格布局也是作用于两级的DOM结构。设置为display: grid
的元素成为一个网格容器,它的子元素则变成网格元素。
<div class="grid">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
<div class="d"></div>
<div class="e"></div>
<div class="f"></div>
</div>
样式部分
.grid{
display: grid; /* 1.设置网格布局 */
grid-template-columns: 1fr 1fr 1fr; /* 2.定义等宽三列 */
grid-template-rows: 1fr 1fr; /* 3.定义等高两行 */
grid-gap: 0.5em; /* 4.单元格间距 */
}
.grid>*{
background-color: #eee;
color: white;
padding: 2em;
border-radius: 0.5em;
}
-
display: grid
:定义网格容器。容器会表现得像一个块级元素,100%填充可用宽度 -
grid-template-columns
:定义了网格每列大小 -
grid-template-rows
:定义了网格每行大小 -
fr
:分数单位。这里也可以使用其他单位。例如:grid-template-columns: 300px 1fr
定义了一个固定宽度为300px的列,后面跟着一个会填满剩余可用空间的列 -
grid-gap
:定义了每个网格单元之间的间距。也可以用两个值分别指定垂直和水平方向的间距,比如:grid-gap: 0.5em 1em
网格剖析
1. 四个概念
前面提及了网格布局的两个基本元素:网格容器和网格元素。下面是另外四个重要概念:
- 网格线——网格线构成了网格的框架
- 网格轨道——一个网格轨道是两条相邻网格线之间的空间。网格有水平轨道(行)和垂直轨道(列)
- 网格单元——网格上的单个空间,水平和垂直的网格轨道交叉重叠的部分
- 网格区域——网格上的矩形区域,由一个到多个网格单元组成。
2. 示例
如果用网格布局实现之前的示例会是怎样?在下图中,虚线标出了每个网格单元的位置。注意,某些部分跨越了好几个网格单元:
现在,用网格实现弹性布局示例需要改一下HTML结构。这个版本的HTML将网页的所有部分都变成了网格元素:头部、菜单(nav)、主区域,还有两个侧边栏。主区域和两个侧边栏都加上了类tile,用来指定元素共有的白色背景和内边距:
<!-- 网格容器 -->
<div class="container">
<!-- 每个网格元素必须是网格容器的子元素 -->
<header>
<h1 class="page-heading">Ink</h1>
</header>
<nav>
<ul class="site-nav">
<li><a href="/">Home</a></li>
<li><a href="/features">Features</a></li>
<li><a href="/pricing">Pricing</a></li>
<li><a href="/support">Support</a></li>
<li class="nav-right">
<a href="/about">About</a>
</li>
</ul>
</nav>
<main class="main tile">
<h1>Team collaboration done right</h1>
<p>Thousands of teams from all over the
world turn to <b>Ink</b> to communicate
and get things done.</p>
</main>
<div class="sidebar-top tile">
<form class="login-form">
<h3>Login</h3>
<p>
<label for="username">Username</label>
<input id="username" type="text"
name="username"/>
</p>
<p>
<label for="password">Password</label>
<input id="password" type="password"
name="password"/>
</p>
<button type="submit">Login</button>
</form>
</div>
<div class="sidebar-bottom tile centered">
<small>Starting at</small>
<div class="cost">
<span class="cost-currency">$</span>
<span class="cost-dollars">20</span>
<span class="cost-cents">.00</span>
</div>
<a class="cta-button" href="/pricing">Sign up</a>
</div>
</div>
样式
:root{
box-sizing: border-box;
}
*,::before,::after{
box-sizing: inherit;
}
body{
background-color: #709b90;
font-family: Arial, Helvetica, sans-serif;
}
.container{
display: grid; /* 定义网格布局 */
grid-template-columns: 2fr 1fr; /* 定义两个垂直网络轨道 */
grid-template-rows: repeat(4,auto); /* 定义四个水平规定 大小为auto */
gap: 1.5em;
max-width: 1080px;
margin: 0 auto;
}
/* 网格元素定位*/
header,nav{
grid-column:1/3;
grid-row:span 1
}
.main{
grid-column: 1/2;
grid-row: 3/5;
}
.sidebar-top{
grid-column: 2/3;
grid-row: 3/4;
}
.sidebar-bottom{
grid-column: 2/3;
grid-row: 4/5;
}
.tile{
padding: 1.5em;
background-color: white;
}
.tile>:first-child{
margin-top: 0;
}
.tile *+*{
margin-top: 1.5em;
}
-
repeat()
:声明多个网格轨道的时候提供了简写方式。比如上面的
grid-template-rows: repeat(4, auto)
定义了四个水平网格轨道,高度为auto,这等价于grid-template-rows: auto auto auto auto
。轨道大小设置为auto,轨道会根据自身内容扩展。用
repeat()
符号还可以定义不同的重复模式,比如repeat(3, 2fr1fr)
会重复三遍这个模式,从而定义六个网格轨道,重复的结果是2fr 1fr 2fr 1fr 2fr 1fr
。
定位-网格线编号
-
grid-column
:用列网格线的编号指定网格元素的位置 -
grid-row
:用列网格线的编号指定网格元素的位置比如,想要一个网格元素在垂直方向上从1号网格线跨越到3号网格线:
grid-column: 1 / 3
这些属性实际上是简写属性:
grid-column
是grid-column-start
和grid-column-end
的简写;grid-row
是grid-row-start
和grid-row-end
的简写。中间的斜线只在简写属性里用于区分两个值
网格线编号+span
除了使用
1/2
跨越网格线,还可以使用span 1
,span表示要跨越几行。如果前面没有指出哪一行,浏览器会根据网格元素的布局算法自动将其放到合适的位置。如果前面加上了网格线编号,它会从指定行开始跨越网格轨道://从第三条网格线跨到第五条网格线: grid-row: 3/5; //span写法。表示从第三条网格线开始,跨两个网络轨道 grid-row: 3/span 2;
与Flexbox配合
弹性布局和网格布局是互补的,它们各自擅长的场景不一样。这两种布局方式有以下两个重要区别:
- Flexbox本质上是一维的,而网格是二维的
- Flexbox是以内容为切入点由内向外工作的,而网格是以布局为切入点从外向内工作的
因为Flexbox是一维的,所以它很适合用在相似的元素组成的行或列上,它支持换行但是没法让上一行元素跟下一行元素对齐。相反,网格是二维的,旨在解决一个轨道的元素跟另一个轨道的元素对齐的问题:
替代语法
1.命名网格线
记编号也许会乱,这时可以给网格线命名。下面声明定义了两列的网格,三条垂直的网格线分别叫作start、center和end:
grid-template-columns:[start] 2fr [center] 1fr [end]
grid-column:start/center //使用
-
给同一个网格线提供多个名称
grid-template-columns:[left-start] 2fr [left-end right-start] 1fr [right-end]
将网格线命名为left-start和left-end,就定义了一个叫作left的区域。
-start
和-end
后缀作为关键字,定义了两者之间的区域。如果给元素设置grid-column: left
,它就会跨越从left-start到left-end的区域
-
以各种方式命名的网格线
比如,将两个网格列列为一组,在每组之前命名一条网格线:
grid-template-columns: repeat(3, [col] 1fr 1fr) //命名 grid-column: col 2 /span 2 //使用,定位在第二组
2.命名网格区域
除了命名网格线,可以直接用命名网格区域将元素定位到网格中。实现这一方法需要借助下面两个属性:
grid-template
(网格容器属性)grid-area
(网格元素属性)
.container{
display: grid;
/* 1.给每个网格单位命名*/
grid-template-areas: "title title"
"nav nav"
"main aside1"
"main aside2";
grid-template-columns: 2fr 1fr;
grid-template-rows: repeat(4,auto);
gap: 1.5em;
max-width: 1080px;
margin: 0 auto;
}
/* 2.将每个网格元素放进一个命名的网格区域 */
header{
grid-area: title;
}
nav{
grid-area: nav;
}
.main{
grid-area: main;
}
.sidebar-top{
grid-area: aside1;
}
.sidebar-bottom{
grid-area: aside2;
}
注意:每个命名的网格区域必须组成一个矩形。
用句点(.)作为名称空出一个网格单元
grid-template-areas: "top top right"
"left . right"
"left bottom bottom"
总结
当构建一个网格时,选择一种舒适的语法即可。网格布局共设计了三种语法:编号的网格线、命名的网格线、命名的网格区域。
隐式网格
在某些场景下,你可能不清楚该把元素放在网格的哪个位置上,比如当元素是从数据库获取时。在这些情况下,以一种宽松的方式定义网格更合理。
这时需要用到隐式网格。使用grid-template-*
属性定义网格轨道时,创建的是显式网格。而当网格元素放在显式轨道外,会自动创建隐式轨道以扩展网格,从而包含这些元素。
指定大小
-
grid-auto-columns
:指定隐性网络列大小(隐式网格轨道默认大小为auto,会扩展到能容纳网格元素内容) -
grid-auto-rows
:指定隐性网络行大小
比如在这个布局中,显性创建列的网格轨道,隐性创建行的网格轨道。这样它能适应任意数量的网格元素。只要照片需要换行显示,就会隐式创建新的一行:
html结构:portfolio网格容器包括网格元素figure,网格元素figure下是一张图与标题。其中featured类来通过跨行跨列来改变图片大小
<div class="portfolio">
<figure class="featured">
<img src="images/01.jpg" alt="monkey" />
<figcaption>Monkey</figcaption>
</figure>
<figure>
<img src="images/02.jpg" alt="eagle" />
<figcaption>Eagle</figcaption>
</figure>
<figure class="featured">
<img src="images/03.jpg" alt="bird" />
<figcaption>Bird</figcaption>
</figure>
<figure>
<img src="images/04.jpg" alt="bear" />
<figcaption>Bear</figcaption>
</figure>
<figure class="featured">
<img src="images/05.jpg" alt="swan" />
<figcaption>Swan</figcaption>
</figure>
<figure>
<img src="images/06.jpg" alt="elephants" />
<figcaption>Elephants</figcaption>
</figure>
</div>
样式:
:root {
box-sizing: border-box;
}
*,
::before,
::after {
box-sizing: inherit;
}
body {
background-color: #709b90;
font-family: Helvetica, Arial, sans-serif;
}
.portfolio {
display: grid;
/* 1.显式创建 将最小列宽设置为300px,自动填充网格 */
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
/* 2.隐式创建 将水平网格轨道的大小设置为1fr 即整个容器 */
grid-auto-rows: 1fr;
grid-gap: 1em;
}
.portfolio .featured{
/*3.放大内容容器,跨两行和两列。目的是为了在后面让图片撑开*/
grid-row: span 2;
grid-column: span 2;
}
.portfolio > figure {
/*4.覆盖默认外边距*/
margin: 0;
}
.portfolio figcaption {
padding: 0.3em 0.8em;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
text-align: right;
}
-
minmax():指定最小尺寸和最大尺寸。浏览器会确保网格轨道的大小介于这两者之间
-
auto-fill:这个关键词表示只要网格放得下,浏览器就会尽可能多地生成轨道。如果网格元素不够填满所有网格轨道,auto-fill就会导致一些空的网格轨道
打开控制台,目前的布局如下。可见目前仍有一些网格没有占据元素被空了出来。这时需要用到下面的属性:
指定布局算法
-
grid-auto-flow:默认情况下,布局算法会按元素在标记中的顺序将其逐列逐行摆放,而这个属性可以控制布局算法的行为。初始值是row,下面的代码使用了
grid-auto-flow: dense
,等价于grid-auto-flow: row dense
.portfolio { ... grid-auto-flow: dense; /*隐式 开启紧凑的网络布局算法*/ }
这时布局如下,空出来的网格已经被元素填上去了。然而此时图片并没有填满网格轨道,这时需要用到下面的方法:
网格元素填满网格轨道
-
Flexbox
默认情况下,每个网格元素都会扩展并填满整个网格区域,但是子元素不会,因此网格区域出现了多余的高度。一个简单的解决办法是用Flexbox,设置每个
<figure>
为弹性容器,方向为column,元素会从上到下垂直排列。然后给图片标签加上flex-grow,强制拉伸图片填充空白区域:.portfolio > figure { margin: 0; display: flex; /*设置弹性布局*/ flex-direction: column; /*设置弹性方向*/ } .portfolio > figure>img { flex-grow: 1; /*弹性元素拉伸*/ max-width: 100%; /*图片最大只能填满空间*/ }
-
object-fit
但是拉伸图片会改变图片的宽高比,可能会导致图片变形。这时可以用到特殊属性
object-fit
。默认情况下,<img>
的object-fit属性值为fill,也就是整个图片会缩放以填满<img>
元素。你也可以设置其他值改变默认行为:-
fill(默认)
-
cover:扩展图片让它填满盒子,导致图片一部分被裁剪
-
contain:缩放图片让它完整填充盒子,导致盒子里出现空白
.portfolio > figure>img { ... object-fit: cover; }
-
特性查询
@supports
规则后面跟着一个小括号包围的声明。如果浏览器理解这个声明,它就会使用大括号里面的所有样式规则。如果它不理解小括号里的声明,就不会使用这些样式规则:
@supports(display:grid){
...样式
}
@supports not(<declaration>)
——只有当不支持查询声明里的特性时才使用里面的样式规则@supports (<declaration>) or (<declaration>)
——查询声明里的两个特性只要有一个支持就使用里面的样式规则@supports (<declaration>) and (<declaration>)
——查询声明里的两个特性都支持才使用里面的样式规则。
对齐
网格布局模块规范里的对齐属性有一些跟弹性布局相同,还有一些是新属性。CSS给网格布局提供了三个水平方向调整属性:justify-content
、justify-items
、justify-self
;还有三个垂直方向对齐属性:align-content
、align-items
、align-self
。
举个例子:它指定了网格容器的高度为1200px,定义了高800px的有效水平网格轨道。
.grid{
display:grid;
height:1200px;
grid-template-row:repeat(4,200px)
}
在这里,align-content
属性可以指定网格轨道如何在剩下的400px空间内分布,它具有以下值:
- start——将网格轨道放到网格容器的上/左(Flexbox里则是flex-start)
- end——将网格轨道放在网格容器的下/右(Flexbox里则是flex-end)
- center——将网格轨道放在网格容器的中间
- stretch——将网格轨道拉伸至填满网格容器
- space-between——将剩余空间平均分配到每个网格轨道之间(它能覆盖任何grid-gap值)
- space-around——将空间分配到每个网格轨道之间,且在两端各加上一半的间距
- space-evenly——将空间分配到每个网格轨道之间,且在两端各加上同等大小的间距(Flexbox规范不支持)
四、定位和上下叠层
position属性可以用来构建下拉菜单、模态框以及现代Web应用程序的一些基本效果。层叠上下文是定位的一个隐藏的副作用
1.静态定位
position:static
是默认定位(静态定位),前面所用的都是这个。而如果元素使用了静态定位,那么就说它未被定位
2.固定定位
position: fixed
让元素相对视口定位,搭配四种属性top、right、bottom和left。这四个值还隐式地定义了元素的宽高。比如指定left: 2em; right: 2em
表示元素的左边缘距离视口左边2em,右边缘距离视口右边2em
示例-创建模态框
<body>
<!-- 1.触发弹框的按钮 -->
<button id="open">打开模态框</button>
<!-- 2.模态框容器 -->
<div class="modal" id="modal">
<!-- 3.蒙层 -->
<div class="modal-backdrop"></div>
<!-- 4.模态框容器 -->
<div class="modal-body">
<!-- 5.关闭弹框按钮 -->
<button id="close" class="modal-close">close</button>
<p>1111111111111111</p>
<p>22222222222222</p>
</div>
</div>
</body>
<script type="text/javascript">
var openModal = document.getElementById('open');
var closeModal = document.getElementById('close');
var modal = document.getElementById('modal');
openModal.addEventListener('click', function(event) {
event.preventDefault();
modal.style.display = 'block';
});
closeModal.addEventListener('click', function(event) {
event.preventDefault();
modal.style.display = 'none';
});
</script>
样式
body {
min-height: 200vh; /* 设置网页高度,让页面出现滚动条(为了体现固定定位) */
margin: 0;
}
button {
padding: .5em .7em;
border: 1px solid #8d8d8d;
background-color: white;
font-size: 1em;
}
.modal {
display: none;
}
/* 蒙层:当打开模态框时,用半透明的蒙层遮挡网页剩余内容 */
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
}
/* 模态框定位 */
.modal-body {
position: fixed;
top: 3em;
bottom: 3em;
right: 20%;
left: 20%;
padding: 2em 3em;
background-color: white;
overflow: auto;
}
因为固定元素从文档流中移除了,所以它不影响页面其他元素的位置。别的元素跟随正常文档流,就像固定元素不存在一样。
控制定位元素的大小
-
指定四个方向的值
position: fixed; top: 3em; bottom: 3em; right: 20%; left: 20%;
-
指定需要的方向值并用width和/或height
position:fixed top:1em; right:1em; width:20%
3.绝对定位
position: absolute
相对最近的祖先定位元素。配合top、right、bottom和left定义元素位置
示例-close按钮
现在将Close按钮放在模态框的右上角。
.modal-close {
position:absolute;
top: 0.3em;
right: 0.3em;
padding: 0.3em;
}
相对于谁,我们就把谁叫做包含块。在本例中,包含块是它的父元素。如果父元素未被定位,那么浏览器会沿着DOM树往上找它的祖父、曾祖父,直到找到一个定位元素。如果祖先元素都没有定位,那么绝对定位的元素会基于初始包含块来定位,初始包含块跟视口一样大,固定在网页的顶部。
定位伪元素
对于关闭按钮,用户通常期望看到一个类似于x的图形化显示。这时可以用CSS隐藏close,并显示x。
- 将按钮的文字挤到外面,隐藏溢出内容
- 将按钮的
::after
伪元素的content属性设置为x,并让伪元素绝对定位到按钮中间
.modal-close {
...
font-size: 2em;
height: 1em; /* 1.让按钮变成小正方形 目的是为了挤出文字*/
width: 1em;
text-indent: 10em; /* 2.挤出文字并隐藏溢出 */
overflow: hidden;
border: 0;
}
.modal-close::after{
position: absolute; /*3.按钮成为伪元素的包含块*/
line-height: 0.5; /* 4.设置一个较小的line-height让伪元素不要太高 */
top: 0.2em; /*5.位置需要自己反复调整*/
left: 0.1em;
text-indent: 0;
content: "\00D7"; /* 6.添加Unicode字符U+00D7(乘法符号) */
cursor: pointer;
}
最后呈现:
4.相对定位
应用position: relative
通常看不到页面有视觉改变。如果加上top、right、bottom和left属性,元素会移走,但是不会改变它周围任何元素的位置。有时可以用这些属性调整相对元素的位置,把它挤到某个位置,但这只是相对定位的一个冷门用法。更常见的用法是使用position: relative
给它里面的绝对定位元素创建一个包含块。
示例-创建下拉菜单
html结构
<!-- 1.下拉菜单容器 -->
<div class="dropdown">
<div class="dropdown-label">Main Menu</div>
<!-- 2.菜单列表容器 -->
<div class="dropdown-menu">
<ul class="submenu">
<li><a href="/">Home</a></li>
<li><a href="/coffees">Coffees</a></li>
<li><a href="/brewers">Brewers</a></li>
<li><a href="/specials">Specials</a></li>
<li><a href="/about">About us</a></li>
</ul>
</div>
</div>
样式
.dropdown {
display: inline-block;
position: relative; /*1.创建包含块*/
}
.dropdown-label {
padding: 0.5em 1.5em;
border: 1px solid #ccc;
background-color: #eee;
cursor: pointer;
}
.dropdown-menu {
display: none; /*2.隐藏菜单列表*/
position: absolute;
left: 0;
top: 2.1em; /*3.将菜单列表移到菜单下面 内边距+字体大小+边框*/
min-width: 100%;
background-color: #eee;
}
/*4.鼠标悬浮展示菜单列表*/
.dropdown:hover .dropdown-menu{
display: block;
}
.submenu {
padding-left: 0;
margin: 0;
list-style-type: none;
border: 1px solid #999;
}
.submenu > li + li {
border-top: 1px solid #999;
}
.submenu > li > a {
display: block;
padding: 0.5em 1.5em;
background-color: #eee;
color: #369;
text-decoration: none;
}
.submenu > li > a:hover {
background-color: #fff;
}
示例-创建CSS三角形
下拉菜单距离完美还差一步。可以用边框画一个三角形当作向下箭头。这里用标签的::after
伪元素来画三角形,然后使用绝对定位将它放到标签的右边。
原理
粗边框如下:
将元素的宽和高缩小到0:
保留顶部边框,其他设置为透明:
实现:
.dropdown-label {
...
/* css三角形-增加右侧内边距,给箭头留出空间 */
padding: 0.5em 2em 0.5em 1.5em;
}
/* css三角形-添加伪元素 */
.dropdown-label::after {
content: "";
position: absolute;
right: 1em;
top: 1em;
border: 0.3em solid;
border-color: black transparent transparent;
}
/* css三角形-翻转 */
.dropdown:hover .dropdown-label::after {
top: 0.7em;
border-color: transparent transparent black;
}
5.层叠上下文
在同一页面定位多个元素时,可能会遇到两个不同定位的元素重叠的现象。
渲染过程与层叠顺序
浏览器将HTML解析为DOM的同时还创建了另一个树形结构,叫作渲染树(render tree)。它代表了每个元素的视觉样式和位置。同时还决定浏览器绘制元素的顺序。
- 未使用定位时,元素在HTML里出现的顺序决定了绘制的顺序,后出现的元素会绘制在先出现的元素前面
- 使用定位时,浏览器会先绘制所有非定位的元素,然后绘制定位元素。即默认情况下,定位元素会出现在非定位元素前
z-index控制层叠顺序
z-index的值越高,表示越在前。使用它时需要注意以下两点:
- z-index只在定位元素上生效,不能用它控制静态元素
- 给一个定位元素加上z-index可以创建层叠上下文
层叠上下文
一个层叠上下文包含一个元素或者由浏览器一起绘制的一组元素。其中一个元素会作为层叠上下文的根,比如给一个定位元素加上z-index的时候,它就变成了一个新的层叠上下文的根。所有后代元素就是这个层叠上下文的一部分。
层叠上下文内的元素会按照以下顺序从后到前叠放:
- 层叠上下文的根
- z-index为负的定位元素(及其子元素)
- 非定位元素
- z-index为auto的定位元素(及其子元素)
- z-index为正的定位元素(及其子元素)
6.粘性定位
position:sticky
粘性定位是相对定位和固定定位的结合体:正常情况下,元素会随着页面滚动,当到达屏幕的特定位置时,如果用户继续滚动,它就会“锁定”在这个位置。最常见的用例是侧边栏导航。
五、响应式设计
原则一 移动优先
构建桌面版之前要先构建移动端布局,确保两个版本都生效。这是因为移动端的限制更多,如果先设计pc端,那么有些效果可能不会在移动端生效
移动端关注内容,其他地方可以隐藏或者放在不起眼的地方
断点:一个特殊的临界值。屏幕尺寸达到这个值时,网页的样式会发生改变,以便给当前屏幕尺寸提供最佳的布局。
示例-起步
<header id="header" class="page-header">
<div class="title">
<h1>Wombat Coffee Roasters</h1>
<div class="slogan">We love coffee</div>
</div>
</header>
<!-- 菜单 -->
<nav class="menu" id="main-menu">
<button class="menu-toggle" id="toggle-menu">
toggle menu
</button>
<div class="menu-dropdown">
<ul class="nav-menu">
<li><a href="/about.html">About</a></li>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/menu.html">Menu</a></li>
<li><a href="/brew.html">Brew</a></li>
</ul>
</div>
</nav>
<!-- 主图 -->
<aside id="hero" class="hero">
Welcome to Wombat Coffee Roasters! We are
passionate about our craft, striving to bring you
the best hand-crafted coffee in the city.
</aside>
<!-- 主体内容 -->
<main id="main">
<div class="row">
<section class="column">
<h2 class="subtitle">Single-origin</h2>
<p>We have built partnerships with small farms
around the world to hand-select beans at the
peak of season. We then carefully roast in
<a href="/batch-size.html">small batches</a>
to maximize their potential.</p>
</section>
<section class="column">
<h2 class="subtitle">Blends</h2>
<p>Our tasters have put together a selection of
carefully balanced blends. Our famous
<a href="/house-blend.html">house blend</a>
is available year round.</p>
</section>
<section class="column">
<h2 class="subtitle">Brewing Equipment</h2>
<p>We offer our favorite kettles, French
presses, and pour-over cones. Come to one of
our <a href="/classes.html">brewing
classes</a> to learn how to brew the perfect
pour-over cup.</p>
</section>
</div>
</main>
css
/* 基础样式*/
:root {
box-sizing: border-box;
/* 字体大小根据视口适当缩放 */
font-size: calc(1vw + 0.6em);
}
*,*::before,*::after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: Helvetica, Arial, sans-serif;
}
/* 链接 */
a:link {
color: #1476b8;
font-weight: bold;
text-decoration: none;
}
a:visited {
color: #1430b8;
}
a:hover {
text-decoration: underline;
}
a:active {
color: #b81414;
}
/* 网页头部和标题 */
.page-header {
padding: 0.4em 1em;
background-color: #fff;
}
.title > h1 {
color: #333;
text-transform: uppercase;
font-size: 1.5rem;
margin: .2em 0;
}
.slogan {
color: #888;
font-size: 0.875em;
margin: 0;
}
/*Hero image */
.hero {
padding: 2em 1em;
text-align: center;
background-image: url(images/01.jpeg);
background-size: 100%;
color: #fff;
text-shadow: 0.1em 0.1em 0.3em #000;
}
/* 主体内容 */
main {
padding: 1em;
}
.subtitle {
margin-top: 1.5em;
margin-bottom: 1.5em;
font-size: 0.875rem;
text-transform: uppercase;
}
效果如图:
示例-创建菜单
汉堡包菜单:它解决了在小屏幕里显示更多内容的问题,但是也有弊端。将重要元素(比如主要的导航菜单)隐藏起来会减少用户跟它们交互的机会
/* 汉堡包菜单 */
.menu {
position: relative; /*1.创建包含块*/
}
.menu-toggle {
position: absolute;
top: -1.2em; /*2.将按钮拉到包含块上面*/
right: 0.1em;
border: 0; /*3.覆盖浏览器按钮样式*/
background-color: transparent;
font-size: 3em;
line-height: 0.4;
text-indent: 5em; /*4.隐藏按钮的文本*/
width: 1em;
height: 1em;
overflow: hidden;
white-space: nowrap;
}
.menu-toggle::after {
position: absolute;
top: 0.2em;
left: 0.2em;
display: block;
content: "\2261"; /*5.汉堡包图标*/
text-indent: 0;
}
.menu-dropdown {
display: none;
position: absolute;
right: 0;
left: 0;
margin: 0;
}
.menu.is-open .menu-dropdown {
display: block; /*6.当加上is-open类的时候显示*/
}
/* 菜单样式 */
.nav-menu {
margin: 0;
padding-left: 0;
border: 1px solid #ccc;
list-style: none;
background-color: #000;
color: #fff;
}
.nav-menu > li + li {
border-top: 1px solid #ccc;
}
.nav-menu > li > a {
display: block;
padding: 0.8em 1em;
color: #fff;
font-weight: normal;
}
html添加事件
<script type="text/javascript">
(function() {
var button = document.getElementById('toggle-menu');
button.addEventListener('click', function(event) {
event.preventDefault();
var menu = document.getElementById('main-menu');
menu.classList.toggle('is-open');
});
})();
</script>
注意:菜单项链接周围的内边距。因为是给移动设备设计,通常是触屏设备,所以关键的点击区域应该足够大,并很容易用一个手指点击
现在效果如下:
示例-添加meta标签
现在移动版设计已经完成,但是还差一个重要细节:视口的meta标签。
这个HTML标签告诉移动设备,你已经特意将网页适配了小屏设备。如果不加这个标签,移动浏览器会假定网页不是响应式的,并且会尝试模拟桌面浏览器
<meta name="viewport" content="width=device-width, initial-scale=1.0">
meta标签的content属性里包含三个选项:
- 告诉浏览器当解析CSS时将设备的宽度作为假定宽度,而不是一个全屏的桌面浏览器的宽度
- 当页面加载时,它使用initial-scale将缩放比设置为100%
- 第三个选项user-scalable=no,阻止用户在移动设备上用两个手指缩放(不常用)
对于第一个选项,可以明确设置width=320让浏览器假定视口宽度为320px,但是通常不建议这样,因为移动设备的尺寸范围很广。
原则二 媒体查询
引入
媒体查询(mediaqueries)允许某些样式只在页面满足特定条件时才生效。这样就可以根据屏幕大小定制样式
媒体查询使用@media规则选择满足特定条件的设备。一条简单的媒体查询如下代码所示。表示只有当设备的视口宽度大于等于560px的时候,才会给标题设置2.25rem的字号。如果视口宽度小于560px,那么里面的所有规则都会被忽略。
@media(min-width:560px){
.title>h1{
font-size:2.25rem
}
}
在媒体查询里更适合用em
em是基于浏览器默认字号的(通常是16px)。下面将560px改成35em(560 / 16)。在这里,560px这个临界值被称为断点。
@media(min-width:35em){
.title>h1{
font-size:2.25rem
}
}
1.媒体查询方式
-
用and关键字联合表示同时满足
@media (min-width:20em)and(max-width:35em){...}
-
用逗号分隔表示只需要满足多个条件之一
@media (min-width:20em),(max-width:35em){...}
2.媒体特征
-
min-width:匹配视口大于特定宽度的设备
-
max-width:匹配视口小于特定宽度的设备
-
min-height:匹配高度大于特定高度的视口
-
max-height:匹配高度小于特定高度的视口
-
orientation: landscape:匹配宽度大于高度的视口
-
orientation: portrait:匹配高度大于宽度的视口
-
min-resolution: 2dppx:匹配屏幕分辨率大于等于2dppx(dppx指每个CSS像素里包含的物理像素点数)的设备,比如视网膜屏幕
-
max-resolution: 2dppx:匹配屏幕分辨率小于等于2dppx的设备
完整的媒体特征列表请访问MDN文档:@media
3.媒体类型
-
@media screen:针对屏幕
-
@media print:可以控制打印时的网页布局,这样就能在打印时去掉背景图(节省墨水),隐藏不必要的导航栏。
为了帮助用户打印网页,需要采取一些通用步骤。大多数情况下,需要将基础打印样式放在@media print {...}媒体查询内。使用display: none隐藏不重要的内容,比如导航菜单和页脚。当用户打印网页时,他们绝大多数情况下只关心网页的主体内容。还可以将整体的字体颜色设置成黑色,去掉文字后面的背景图片和背景色。大多数情况下,用通用选择器就能实现。下面的代码使用了!important,这样就不必担心被后面的代码覆盖
@media print{ *{ color:black !importent; background:none !importent; } }
给网页添加断点
@media (min-width:35em){
.title>h1{
...
}
}
@media (min-width:50em){
.title>h1{
...
}
}
示例-中屏断点
现在想在较大的屏幕中实现下面的布局:
@media (min-width:35em){
.title>h1{
font-size: 2.25rem;
}
}
@media (min-width: 35em) {
.page-header {
padding: 1em; /*增加头部内边距*/
}
}
@media (min-width: 35em) {
.hero {
padding: 5em 3em; /*增加主图的内边距和字号*/
font-size: 1.2rem;
}
}
@media (min-width: 35em) {
main {
padding: 2em 1em; /*增加主元素内边距*/
}
}
接下来处理菜单样式。首先,要将下拉菜单的打开和关闭行为去掉;其次将菜单从垂直排列改为水平排列布局。
/* 把菜单的切换按钮隐藏,让下拉菜单的内容显示 */
@media (min-width: 35em) {
.menu-toggle {
display: none;
}
.menu-dropdown {
display: block;
/* 覆盖绝对定位 */
position: static;
}
}
/* 将菜单改为弹性容器,让菜单子元素扩展,填满屏幕宽度 */
@media (min-width: 35em) {
.nav-menu {
display: flex;
border: 0;
padding: 0 1em;
}
.nav-menu > li {
flex: 1;
}
.nav-menu > li + li {
border: 0;
}
.nav-menu > li > a {
padding: 0.3em;
text-align: center;
}
}
响应式的列
@media (min-width: 35em) {
.row {
display: flex; /*实现等宽列*/
/* 使用负外边距将容器扩大,补偿列的外边距 */
margin-left: -.75em;
margin-right: -.75em;
}
.column {
flex: 1; /*实现等宽列*/
margin-right: 0.75em; /*添加列间距*/
margin-left: 0.75em;
}
}
现在效果如图:
原则三 流式布局
引入
流式布局,有时被称作液体布局(liquid layout),指的是使用的容器随视口宽度而变化。
与固定布局相反。固定布局的列都是用px或者em单位定义。比如设定了800px宽的元素在小屏上如果超出视口范围,会出现水平滚动条,而流式容器会自动缩小以适应视口。
在流式布局中,主页面容器通常不会有明确宽度,也不会给百分比宽度,但可能会设置左右内边距,或者设置左右外边距为auto,让其与视口边缘之间产生留白。
在主容器中,任何列都用百分比来定义宽度。这样无论屏幕宽度是多少都能放得下主容器。用Flexbox布局也可以,设置弹性元素的flex-grow和flex-shrink(更重要),让元素能够始终填满屏幕。要习惯将容器宽度设置为百分比,而不是任何固定的值。
网页默认就是响应式的。没添加CSS的时候,块级元素不会比视口宽,行内元素会折行,从而避免出现水平滚动条。加上CSS样式后,就需要你来维护网页的响应式特性了。
示例-大屏断点
接下来要为下一个屏幕断点加上媒体查询,大视口下的网页布局最终效果如图:
/* 大屏断点 */
@media (min-width: 50em) {
.page-header {
padding: 1em 4em;
}
}
@media (min-width: 50em) {
.hero {
padding: 7em 6em;
}
}
@media (min-width: 50em) {
main {
padding: 2em 4em;
}
}
@media (min-width: 50em) {
.nav-menu {
padding: 0 4em;
}
}
@media (min-width: 50em) {
:root {
font-size: 1.125em;
}
}
处理表格
html部分
<table>
<thead>
<tr>
<th>Country</th>
<th>Region/Farm</th>
<th>Tasting notes</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Nicaragua</td>
<td>Matagulpa</td>
<td>Dark chocolate, almond</td>
<td>$13.95</td>
</tr>
<tr>
<td>Ethiopia</td>
<td>Yirgacheffe</td>
<td>Sweet tea, blueberry</td>
<td>$15.95</td>
</tr>
<tr>
<td>Ethiopia</td>
<td>Nano Challa</td>
<td>Tangerine, jasmine</td>
<td>$14.95</td>
</tr>
</tbody>
</table>
如果表格的列太多,很容易超过屏幕宽度:
有一个办法是将表格强制显示为一个普通的块级元素:
这个布局由<table>
、<tr>
、<td>
元素组成,现在对它们使用了display: block声明,覆盖了正常的table、table-row、table-cell的显示值。现在配合max-width媒体查询限制在小屏下才改变表格元素的显示:
table {
border-collapse: collapse;
}
th, td {
border: 1px solid black;
padding: 0.3em 0.5em;
}
table {
width: 100%;
}
@media (max-width: 30em) {
table, thead, tbody, tr, th, td {
display: block;
}
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
tr {
margin-bottom: 1em;
}
}
响应式图片
在响应式设计中,图片需要特别关注。不仅要让图片适应屏幕,还要考虑移动端用户的带宽限制。
-
保证图片充分压缩。在图片编辑器中选择“Save forWeb”选项能够极大地减小图片体积,或者用别的图片压缩工具压缩图片,比如tinypng网站
-
避免不必要的高分辨率图片,而是否必要则取决于视口大小。也没有必要为小屏幕提供大图,因为大图最终会被缩小。
不同视口大小使用不同的图片
-
创建不同分辨率的副本
.hero { padding: 2em 1em; text-align: center; background-image: url(coffee-beans-small.jpg); background-size: 100%; color: #fff; text-shadow: 0.1em 0.1em 0.3em #000; } @media (min-width: 35em) { .hero { padding: 5em 3em; font-size: 1.2rem; background-image: url(coffee-beans-medium.jpg); } } @media (min-width: 50em) { .hero { padding: 7em 6em; background-image: url(coffee-beans.jpg); } }
-
使用srcset提供对应的图片
这个属性是HTML的一个较新的特性。它可以为一个
<img>
标签指定不同的图片URL,并指定相应的分辨率。浏览器会根据自身需要决定加载哪一个图片<img alt="A white coffee mug on a bed of coffee beans" src="coffee-beans-small.jpg" srcset="coffee-beans-small.jpg 560w, coffee-beans-medium.jpg 800w, coffee-beans.jpg 1280w">
有关响应式图片的更多内容,请访问jakearchibald网站上的文章The Anatomy of Responsive Images。