《深入解析css》 |精通布局

前言:所有内容与示例源码源于基思·J·格兰特的《深入解析css》,文章用于笔记整理。源码仓库

一、浮动布局

引入

浮动元素会被移出正常文档流,文档流会重新排列并包围浮动元素。这种布局在报纸和杂志中很常见。

image.png

容器折叠

浮动元素的高度不会加到父元素上。所以当子元素的高度比父元素大时,父元素不会撑开。解决办法:使用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>

双容器模式居中法

image.png

清除浮动

  • 在父元素里的末尾加上一个空标签(如上)

  • 用伪元素清除浮动

    .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
    }
    

浮动陷阱

浏览器会将浮动元素尽可能地放在靠上的地方,所以有时候会发生这种情况:

image.png

解决办法:清除每行的第一个元素上面的浮动。假设你一行需要放两个浮动元素,那么你可以这样写:

.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
}

image.png

上面的方式就是给正文建立一个块级格式化上下文(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,该元素变成了一个弹性容器,它的直接子元素变成了弹性子元素。下面是三个概念的一些说明:弹性容器、弹性子元素、两条轴:

  • 弹性子元素默认是在同一行按照从左到右的顺序并排排列
  • 弹性容器像块元素一样填满可用宽度
  • 弹性子元素不一定填满其弹性容器的宽度
  • 弹性子元素高度相等,该高度由它们的内容决定
  • 弹性容器能控制内部元素的布局
  • 子元素按照主轴线排列,主轴的方向从左到右。副轴的方向从上到下。可改变

image.png

示例

image.png

结构

<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;
}

image.png

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;
}

image.png

目前还没有特别设置两列的宽度,所以是根据内容自适应的宽度。可见没有完全填满可用空间。本例用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值计算出来后,它们(加上子元素之间的外边距)加起来会占据一定的宽度。加起来的宽度不一定正好填满弹性容器的宽度,可能会有留白,如下图:

    image.png

    每个弹性子元素的初始主尺寸确定后,它们可能需要在主轴方向扩大或者缩小来适应(或者填充)弹性容器的大小。对于上面的情况,可能需要flex-grow和flex-shrink来决定缩放的规则。

  • flex-grow:增长因子。多出来的留白会按照flex-grow的值分配给每个弹性子元素,值为非负整数。如果一个弹性子元素的flex-grow值为0,那么它的宽度不会超过flex-basis的值;如果某个弹性子元素的增长因子非0,那么这些元素会增长到所有的剩余空间被分配完,也就意味着弹性子元素会填满容器的宽度

    image.png

    flex-grow的值越大,元素的“权重”越高,也就会占据更大的剩余宽度:

    image.png

  • flex-shrink:防止溢出。计算出弹性子元素的初始主尺寸后,它们的累加值可能会超出弹性容器的可用宽度。如果不用flex-shrink,就会导致溢出

    image.png

    每个子元素的flex-shrink值代表了它是否应该收缩以防止溢出。如果某个子元素为flex-shrink: 0,则不会收缩;如果值大于0,则会收缩至不再溢出。按照flex-shrink值的比例,值越大的元素收缩得越多。


实际应用

image.png

弹性方向

弹性布局的另一个重要功能是能够切换主副轴方向,用弹性容器的flex-direction属性控制。如前面的例子,它的初始值是row

image.png

现在的布局有一个隐藏的缺陷,给主板块添加更多内容,会发现主板块超出了右边板块的底部。Flexbox应该能让两列的高度相等,为什么不起作用了呢?

image.png

其实左右两个弹性子元素是等高的。问题是右边栏内部的两个板块没有扩展到填满右边栏区域。如果想让两列扩展到填满容器的高度。要将右边栏(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 image.png
flex-end image.png
center image.png
space-between image.png
space-around image.png
align-items 控制子元素在副轴上的位置
flex-start image.png
flex-end image.png
center image.png
stretch(初始) image.png
baseline image.png
align-content 如果开启了flex-wrap,align-content就会控制
弹性子元素在副轴上的间距。
如果子元素没有换行,会忽略该属性
flex-start image.png
flex-end image.png
center image.png
stretch image.png
space-between image.png
space-around image.png

弹性子元素的属性

flex-grow 指定“增长因子”,填充未使用空间 image.png
flex-shrink 指定“收缩因子”,防止溢出
如果弹性容器开启了flex-wrap,则会忽略该属性
image.png
flex-basis 指定子元素初始大小
flex flex-grow、flex-shrink、shrink的缩写
align-self 控制子元素在副轴上的对齐方式
会覆盖容器上的align-items值
若元素副轴方向上的外边距为auto,则忽略
auto image.png
flex-start image.png
flex-end image.png
center image.png
stretch image.png
baseline image.png
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网格可以定义由行和列组成的二维布局,然后将元素放置到网格中。网格的大小既可以精确定义,也可以根据自身内容自动计算。可以将元素精确地放置到网格某个位置,也可以让其在网格内自动定位,填充划分好的区域。

image.png

与弹性布局对比,浏览器实现弹性布局的早期版本时,加浏览器前缀才能使用。随着规范的演变,浏览器更新了实现方式,开发人员也必须相应地更新自己的代码,但是还要将以前的代码保留以支持旧版的浏览器,这导致Flexbox问世的经历十分坎坷。与此同时,浏览器几乎可以完全实现网格布局。

构建基础网格

现在先创建一个简单的网格布局,以确认浏览器支持该特性。跟Flexbox类似,网格布局也是作用于两级的DOM结构。设置为display: grid的元素成为一个网格容器,它的子元素则变成网格元素

image.png

<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. 四个概念

前面提及了网格布局的两个基本元素:网格容器和网格元素。下面是另外四个重要概念:

image.png

  • 网格线——网格线构成了网格的框架
  • 网格轨道——一个网格轨道是两条相邻网格线之间的空间。网格有水平轨道(行)和垂直轨道(列)
  • 网格单元——网格上的单个空间,水平和垂直的网格轨道交叉重叠的部分
  • 网格区域——网格上的矩形区域,由一个到多个网格单元组成。

2. 示例

如果用网格布局实现之前的示例会是怎样?在下图中,虚线标出了每个网格单元的位置。注意,某些部分跨越了好几个网格单元:

image.png

现在,用网格实现弹性布局示例需要改一下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

定位-网格线编号

image.png

  • grid-column:用列网格线的编号指定网格元素的位置

  • grid-row:用列网格线的编号指定网格元素的位置

    比如,想要一个网格元素在垂直方向上从1号网格线跨越到3号网格线:grid-column: 1 / 3

    这些属性实际上是简写属性:grid-columngrid-column-startgrid-column-end的简写;grid-rowgrid-row-startgrid-row-end的简写。中间的斜线只在简写属性里用于区分两个值


    网格线编号+span

    除了使用1/2跨越网格线,还可以使用span 1,span表示要跨越几行。如果前面没有指出哪一行,浏览器会根据网格元素的布局算法自动将其放到合适的位置。如果前面加上了网格线编号,它会从指定行开始跨越网格轨道:

    //从第三条网格线跨到第五条网格线:
    grid-row: 3/5;  
    
    //span写法。表示从第三条网格线开始,跨两个网络轨道
    grid-row: 3/span 2;
    

与Flexbox配合

弹性布局和网格布局是互补的,它们各自擅长的场景不一样。这两种布局方式有以下两个重要区别:

  • Flexbox本质上是一维的,而网格是二维的
  • Flexbox是以内容为切入点由内向外工作的,而网格是以布局为切入点从外向内工作的

因为Flexbox是一维的,所以它很适合用在相似的元素组成的行或列上,它支持换行但是没法让上一行元素跟下一行元素对齐。相反,网格是二维的,旨在解决一个轨道的元素跟另一个轨道的元素对齐的问题:

image.png

替代语法

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的区域

  • 以各种方式命名的网格线

    比如,将两个网格列列为一组,在每组之前命名一条网格线:

    image.png

    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:指定隐性网络行大小

比如在这个布局中,显性创建列的网格轨道,隐性创建行的网格轨道。这样它能适应任意数量的网格元素。只要照片需要换行显示,就会隐式创建新的一行:

image.png

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就会导致一些空的网格轨道


打开控制台,目前的布局如下。可见目前仍有一些网格没有占据元素被空了出来。这时需要用到下面的属性:

image.png

指定布局算法

  • grid-auto-flow:默认情况下,布局算法会按元素在标记中的顺序将其逐列逐行摆放,而这个属性可以控制布局算法的行为。初始值是row,下面的代码使用了grid-auto-flow: dense,等价于grid-auto-flow: row dense

    .portfolio {
      ...
      grid-auto-flow: dense;  /*隐式 开启紧凑的网络布局算法*/
    }
    

    这时布局如下,空出来的网格已经被元素填上去了。然而此时图片并没有填满网格轨道,这时需要用到下面的方法:

    image.png

网格元素填满网格轨道

  • Flexbox

    默认情况下,每个网格元素都会扩展并填满整个网格区域,但是子元素不会,因此网格区域出现了多余的高度。一个简单的解决办法是用Flexbox,设置每个<figure>为弹性容器,方向为column,元素会从上到下垂直排列。然后给图片标签加上flex-grow,强制拉伸图片填充空白区域:

    .portfolio > figure {
    margin: 0;        
    display: flex;			/*设置弹性布局*/
    flex-direction: column;	/*设置弹性方向*/
    }
    
    .portfolio > figure>img {
    flex-grow: 1;			/*弹性元素拉伸*/
    max-width: 100%;		/*图片最大只能填满空间*/
    }
    

    image.png

  • object-fit

    但是拉伸图片会改变图片的宽高比,可能会导致图片变形。这时可以用到特殊属性object-fit。默认情况下,<img>的object-fit属性值为fill,也就是整个图片会缩放以填满<img>元素。你也可以设置其他值改变默认行为:

    1. fill(默认)

    2. cover:扩展图片让它填满盒子,导致图片一部分被裁剪

    3. contain:缩放图片让它完整填充盒子,导致盒子里出现空白

    image.png

    .portfolio > figure>img {
      ...
      object-fit: cover;
    }
    

特性查询

@supports规则后面跟着一个小括号包围的声明。如果浏览器理解这个声明,它就会使用大括号里面的所有样式规则。如果它不理解小括号里的声明,就不会使用这些样式规则:

@supports(display:grid){
    ...样式
}
  • @supports not(<declaration>)——只有当不支持查询声明里的特性时才使用里面的样式规则
  • @supports (<declaration>) or (<declaration>)——查询声明里的两个特性只要有一个支持就使用里面的样式规则
  • @supports (<declaration>) and (<declaration>)——查询声明里的两个特性都支持才使用里面的样式规则。

对齐

网格布局模块规范里的对齐属性有一些跟弹性布局相同,还有一些是新属性。CSS给网格布局提供了三个水平方向调整属性justify-contentjustify-itemsjustify-self;还有三个垂直方向对齐属性align-contentalign-itemsalign-self

image.png

举个例子:它指定了网格容器的高度为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;
}

最后呈现:

image.png

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伪元素来画三角形,然后使用绝对定位将它放到标签的右边。

原理

粗边框如下:

image.png

将元素的宽和高缩小到0:

image.png

保留顶部边框,其他设置为透明:

image.png
实现:

.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;
}

效果如图:

image.png

示例-创建菜单

汉堡包菜单:它解决了在小屏幕里显示更多内容的问题,但是也有弊端。将重要元素(比如主要的导航菜单)隐藏起来会减少用户跟它们交互的机会

image.png

/* 汉堡包菜单 */
.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>

注意:菜单项链接周围的内边距。因为是给移动设备设计,通常是触屏设备,所以关键的点击区域应该足够大,并很容易用一个手指点击

现在效果如下:

image.png

示例-添加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{
    ...
  }
}

示例-中屏断点

现在想在较大的屏幕中实现下面的布局:

image.png

@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;
   }
 }

现在效果如图:

image.png

原则三 流式布局

引入

流式布局,有时被称作液体布局(liquid layout),指的是使用的容器随视口宽度而变化。

与固定布局相反。固定布局的列都是用px或者em单位定义。比如设定了800px宽的元素在小屏上如果超出视口范围,会出现水平滚动条,而流式容器会自动缩小以适应视口。

在流式布局中,主页面容器通常不会有明确宽度,也不会给百分比宽度,但可能会设置左右内边距,或者设置左右外边距为auto,让其与视口边缘之间产生留白。

在主容器中,任何列都用百分比来定义宽度。这样无论屏幕宽度是多少都能放得下主容器。用Flexbox布局也可以,设置弹性元素的flex-grow和flex-shrink(更重要),让元素能够始终填满屏幕。要习惯将容器宽度设置为百分比,而不是任何固定的值。

网页默认就是响应式的。没添加CSS的时候,块级元素不会比视口宽,行内元素会折行,从而避免出现水平滚动条。加上CSS样式后,就需要你来维护网页的响应式特性了

示例-大屏断点

接下来要为下一个屏幕断点加上媒体查询,大视口下的网页布局最终效果如图:

image.png

 /* 大屏断点 */
 @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>

如果表格的列太多,很容易超过屏幕宽度:

image.png

有一个办法是将表格强制显示为一个普通的块级元素:

image.png

这个布局由<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。

posted @ 2020-09-16 20:14  sanhuamao  阅读(505)  评论(0编辑  收藏  举报