zoukankan      html  css  js  c++  java
  • 《深入解析css》—精通布局

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

    一、浮动布局

    引入

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

    image.png

    容器折叠

    浮动元素的高度不会加到父元素上。所以当子元素的高度比父元素大时,父元素不会撑开。解决办法:使用clear属性。将一个元素放在主容器的末尾,并对它使用clear,这会让容器扩展到浮动元素下面

    <style>
        .container{
            max-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 {  8.3333%; }		// 1/12
    .column-2 {  16.6667%; }		// 2/12
    .column-3 {  25%; }			// 3/12等
    .column-4 {  33.3333%; }
    .column-5 {  41.6667%; }
    .column-6 {  50%; }
    .column-7 {  58.3333%; }
    .column-8 {  66.6667%; }
    .column-9 {  75%; }
    .column-10 {  83.3333%; }
    .column-11 {  91.6667% }
    .column-12 {  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- 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;
       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- 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- 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- 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;
      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.让按钮变成小正方形 目的是为了挤出文字*/
       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: "0D7";  /* 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- 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.隐藏按钮的文本*/
       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-560px){
        .title>h1{
            font-size:2.25rem
        }
    }
    

    在媒体查询里更适合用em

    em是基于浏览器默认字号的(通常是16px)。下面将560px改成35em(560 / 16)。在这里,560px这个临界值被称为断点。

      @media(min-35em){
          .title>h1{
              font-size:2.25rem
          }
      }
    

    1.媒体查询方式

    • 用and关键字联合表示同时满足

      @media (min-20em)and(max-35em){...}
      
    • 用逗号分隔表示只需要满足多个条件之一

      @media (min-20em),(max-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-35em){
      .title>h1{
        ...
      }
    }
    @media (min-50em){
      .title>h1{
        ...
      }
    }
    

    示例-中屏断点

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

    image.png

    @media (min-35em){
      .title>h1{
        font-size: 2.25rem;
      }
    }
    @media (min- 35em) {
      .page-header {
        padding: 1em; /*增加头部内边距*/
      }
    }
    @media (min- 35em) {
      .hero {
        padding: 5em 3em; /*增加主图的内边距和字号*/
        font-size: 1.2rem;
      }
    }
    @media (min- 35em) {
      main {
        padding: 2em 1em; /*增加主元素内边距*/
      }
    }
    

    接下来处理菜单样式。首先,要将下拉菜单的打开和关闭行为去掉;其次将菜单从垂直排列改为水平排列布局。

    /* 把菜单的切换按钮隐藏,让下拉菜单的内容显示 */
    @media (min- 35em) {
      .menu-toggle {
        display: none;
      }
      .menu-dropdown {
        display: block;
        /* 覆盖绝对定位 */
        position: static;
      }
    }
    
    /* 将菜单改为弹性容器,让菜单子元素扩展,填满屏幕宽度 */
    @media (min- 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- 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- 50em) {
      .page-header {
        padding: 1em 4em;
      }
    }
    @media (min- 50em) {
      .hero {
        padding: 7em 6em;
      }
    }
    @media (min- 50em) {
      main {
        padding: 2em 4em;
      }
    }
    @media (min- 50em) {
      .nav-menu {
        padding: 0 4em;
      }
    }
    @media (min- 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 {
       100%;
    }
    @media (max- 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- 35em) {
        .hero {
          padding: 5em 3em;
          font-size: 1.2rem;
          background-image: url(coffee-beans-medium.jpg);
        }
      }
      
      @media (min- 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。

  • 相关阅读:
    查找大文件的命令
    JavaScript对象参考手册
    Thymeleaf(Java模板引擎)
    C#基础语法补充
    mysql 拾遗提高(函数、事务、索引)
    jQuery总结
    Hibernate (开源对象关系映射框架)
    git的使用命令
    Xpath,XQuery,DTD
    XML DOM(Document Object Model)
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/13681147.html
Copyright © 2011-2022 走看看