CSS盒模型

681 阅读7分钟

前言

web网页的设计中有三个东西至关重要:主管页面内容的html、主管页面样式布局的css,以及主管动态页面设计的js。这篇文章就从 CSS 中的盒模型开始谈起。众所周知,任何一个 web 页面都是由一堆标签组成的。而在html语言的抽象中,每个标签都有各自的内容和属性,被称为 html 元素。这标志了页面的内容逻辑和每个标签各自的属性。那么,在 CSS 中,每个标签都是如何抽象的呢?

盒模型

在CSS中,每一个元素都被抽象成一个 “盒子”。换句话说,以 css 的观点来看,web 页面本质上就是一堆“盒子”的组合。设计者可以单独设置每个盒子的属性,也可以在大盒子里面设置小盒子的排列方式。

单个盒子

CSS 中每个盒模型都可以抽象成一个三维立体的结构。如图所示,在浏览器展示的平面内,盒模型的结构由内到外包含content(html元素的内容)、padding(内边距,即 html 元素外边缘到边框内边缘之间的距离)、border(边框)、margin(外边距,即边框外边缘到下一个元素边框外边缘的距离) CSS盒模型.png
除了整体设置各部分的属性之外,还可以分别设置四个方向的属性: 盒模型_分开.png 在 z 轴方向,即垂直于平面的方向,padding、content、background-image,background-color,构成了多重层叠关系。除了单个盒子自身的重叠,也可能会有多个元素的位置重叠在一起的情况,为了解决不同元素的重叠问题,需要设置z-index属性。

盒子分类

在web中,有两种常见的盒子:内联盒子(inline box)和块盒子(block box)。

  • 内联盒子(inline box):在默认情况下,高度和宽度都有内容来决定。
  • 块盒子(block box):在默认情况下,其宽度会占据整个容器的宽度,高度则有元素内容决定。 借助图形来帮助理解: 块盒子_内联盒子.png 也就是说,块盒子是多个内联盒子放在一起组成的。在web网页中,内联盒子不会单独占一行,而是占据块盒子的一部分;与之相反,块盒子在web网页中作为一个整体出现。简单来讲,就是定义一整个段落的元素在CSS中用块盒子来抽象,定义段内文本的元素在CSS中用内联盒子来进行抽象。

常见的块级元素和内联元素

  1. 块级元素(block):
  • address – 地址 
  • blockquote – 块引用 
  • dir – 目录列表 
  • div – 常用块级容易,也是CSS layout的主要标签 
  • dl – 定义列表 
  • fieldset – form控制组 
  • form – 交互表单 
  • h1 – h6 标题 
  • hr – 水平分隔线 
  • menu – 菜单列表 
  • ol – 有序表单 
  • p – 段落 
  • pre – 格式化文本 
  • table – 表格 
  • ul – 无序列表 
  • li
  1. 内联元素(inline):
  • a – 锚点 
  • abbr – 缩写 
  • b – 粗体(不推荐) 
  • big – 大字体 
  • br – 换行 
  • cite – 引用 
  • code – 计算机代码(在引用源码的时候需要) 
  • em – 强调 
  • font – 字体设定(不推荐) 
  • i – 斜体 
  • img – 图片 
  • input – 输入框 
  • kbd – 定义键盘文本 
  • label – 表格标签 
  • q – 短引用 
  • span – 常用内联容器,定义文本内区块 
  • strong – 粗体强调 
  • textarea – 多行文本输入框 

盒子排列

在介绍盒子的排列之前,有两个概念需要明白

x 轴:表示web水平方向的轴
y 轴:表示web垂直方向的轴

在web网页中,盒子的排列方式和书写的习惯相关,按照简体中文和英文的书写习惯为例。由于简体中文和英文的书写习惯都是从左往右的。因此,内联盒子是从左往右进行排列的;相对的,块盒子的排列方式则是从上往下。 inline_block排列方向.png 在这里,我们把内联元素排列方向对应的坐标轴(这里是x轴)称为内联轴;把块级元素排列方向对应的坐标轴(这里是y轴)称为块轴
应用内联轴和块轴,可以将盒模型的位置属性转换为逻辑属性:

  • top -> block-start
  • bottom -> blcok-end
  • left -> inline-start
  • right -> inline-end 和模型的逻辑属性类似下图这样表示: 盒模型逻辑属性.png 同样的,盒模型大小的定义也会发生相应的变化:
  • height -> block-size
  • width -> inline-size

display 属性

在介绍完盒模型的相关内容之后,接下来应该关心的一个问题就是盒模型该怎样应用。CSS把html抽象成盒模型,除了能够分别设置每个盒子的相关属性之外,更为关键的应该是在web页面中,这些盒子是如何堆砌的。display属性回答了这个问题。 display元素.png 如图所示,display属性管理着某个特定的 box 的内部和外部的显示类型,也就是说它跟web页面的布局息息相关。其关键字的取值分为六类。

  • display-outside:
    • block:生成块级元素盒子,同时在该元素前后换行
    • inline:生成内联盒子,前后不换行
    • run-in:如果兄弟是块级元素,则该元素生成内联盒子,并且与第一个兄弟元素同行;如果兄弟元素为内联元素,则该元素生成块盒子
  • display-inside:
    • flow:元素使用流布局(块和内联布局),处于试验阶段的属性值
    • flow-root:生成一个块元素框,并且建立新的格式化上下文。常用来清除浮动、解决高度塌陷问题、去除margin合并、实现两栏自适应布局
    • table:实现表格布局
    • flex:实现弹性盒子布局
    • grid:实现网格布局
    • ruby:添加拼音效果
  • display-listitem:实现列表元素效果
  • display-internal:设置 display:tabledisplay:ruby 的内部属性
  • display-box:
    • none: 隐藏盒子,不影响布局
    • content: 不显示该盒子,显示伪盒子或者子顶盒子。
  • display-legacy:对内联盒子的替换,即外部布局为内联布局,内部布局方式为破折号后面的布局方式。主要有:inline-block,inline-table,inline-flex,inline-grid. 关于display属性的更多内容参看MDN文档中的介绍:developer.mozilla.org/en-US/docs/…

content-box V.S. boerder-box

针对这个话题,首先从一个例子开始谈起:

<!DOCTYPE HTML>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <title>cSS布局和display属性</title>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
            .wrapper {
                width: 960px;
                margin-left: auto;
                margin-right: auto;
                color: #fff;
                font-size: 30px;
                text-align: center;
            }
            #header {
                height: 100px;
                background: #38382e;
                margin-bottom: 10px;
            }
            #sidebar {
                float: left;
                width: 220px;
                margin-right: 20px;
                margin-bottom: 10px;
                height: 300px;
                background: #5d33cf;
            }
            #content {
                float: left;
                width: 720px;
                height: 300px;
                background: #c8ca30;
            }
            #fooder {
                background: #cc4a5d;
                height: 100px;
                clear: both;
            }
        </style>
    </head>
    <body>
        <div class="wrapper">
            <div id="header">页眉</div>
            <div id="sidebar">侧边栏</div>
            <div id="content">主内容</div>
            <div id="fooder">页脚</div>
        </div>
       
    </body>

</html>

上述代码描绘的是一个web页面的布局,运行的结果如下图所示: 运行结果_1.png 这个运行结果乍一看是没有什么问题的,但是如果我们改一下两个div之间的内边距或者外边距,即将代码改成如下的样子:

<!DOCTYPE HTML>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <title>cSS布局和display属性</title>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
            .wrapper {
                width: 960px;
                margin-left: auto;
                margin-right: auto;
                color: #fff;
                font-size: 30px;
                text-align: center;

                /* 添加背景颜色 */
                background: #ccc;
            }
            #header {
                height: 100px;
                background: #38382e;
                margin-bottom: 10px;

                /* 添加 10px 的内边距 */
                padding: 10px;
                width: 100%;
            }
            #sidebar {
                float: left;
                width: 220px;
                margin-right: 20px;
                margin-bottom: 10px;
                height: 300px;
                background: #5d33cf;

                /* 添加 10px 的内边距 */
                padding: 10px;
            }
            #content {
                float: left;
                width: 720px;
                height: 300px;
                background: #c8ca30;

                /* 添加 10px 的内边距 */
                padding: 10px;
            }
            #fooder {
                background: #cc4a5d;
                height: 100px;
                clear: both;

                /* 添加 10px 的内边距 */
                padding: 10px;
                width: 100%;
            }
        </style>
    </head>
    <body>
        <div class="wrapper">
            <div id="header">页眉</div>
            <div id="sidebar">侧边栏</div>
            <div id="content">主内容</div>
            <div id="fooder">页脚</div>
        </div>
       
    </body>

</html>

那么,他到运行结果就变成了: 运行结果_2.png 显然,之前的排版全部乱掉了。那么,问题来了。为什么会出现这样的情况呢?
这要从盒模型元素大小和元素的空间尺寸开始说起了:

  • W3C 标准盒模型:
    • 元素的空间尺寸计算:

    element空间高度 = 内容高度 + 内边距 + 边框宽度 + 外边距
    element空间宽度 = 内容宽度 + 内边距 + 边框宽度 + 外边距

    • 元素的大小:

    element宽度 = 内容宽度 + 内边距 + 边框宽度
    element高度 = 内容高度 + 内边距 + 边框宽度 从这几个公式就可以很容易明白,只要添加了内边距,那么这个内边距就会被计算到元素的大小里面。我们之前计算的刚刚好的边框的元素的大小距离就会超出外层容器元素的大小,那么我们的排版自然也就被打乱了。
    现在,为了解决这个问题。box-sizing就华丽出场。语法如下:

box-sizing : content-box | border-box | inherit;
  • content-box:默认值,维持标准盒模型
  • border-box:元素内容的宽度或者高度包含了元素的border、padding、内容的宽度和高度,即满足下述不等式内容的宽度或高度 = 盒子的宽度或高度 - 边框 - 内边距
  • inherit:继承父元素的盒模型模式 接下来就利用 box-sizing:border-box 对代码进行修改:
<!DOCTYPE HTML>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <title>cSS布局和display属性</title>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
            .wrapper {
                width: 960px;
                margin-left: auto;
                margin-right: auto;
                color: #fff;
                font-size: 30px;
                text-align: center;

                /* 添加背景颜色 */
                background: #ccc;
            }
            #header {
                height: 100px;
                background: #38382e;
                margin-bottom: 10px;

                /* 添加 10px 的内边距 */
                padding: 10px;
                width: 100%;
                border: 10px solid red;

                /* 利用 box-sizing 进行修改 */
                -moz-box-sizing: border-box;
                -webkit-box-sizing: border-box;
                -o-box-sizing: border-box;
                -ms-box-sizing: border-box;
                box-sizing: border-box;
            }
            #sidebar {
                float: left;
                width: 220px;
                margin-right: 20px;
                margin-bottom: 10px;
                height: 300px;
                background: #5d33cf;
                
                /* 添加 10px 的内边距 */
                padding: 10px;
                border: 10px solid red;

                /* 利用 box-sizing 进行修改 */
                -moz-box-sizing: border-box;
                -webkit-box-sizing: border-box;
                -o-box-sizing: border-box;
                -ms-box-sizing: border-box;
                box-sizing: border-box;
            }
            #content {
                float: left;
                width: 720px;
                height: 300px;
                background: #c8ca30;

                /* 添加 10px 的内边距 */
                padding: 10px;
                border: 10px solid red;

                /* 利用 box-sizing 进行修改 */
                -moz-box-sizing: border-box;
                -webkit-box-sizing: border-box;
                -o-box-sizing: border-box;
                -ms-box-sizing: border-box;
                box-sizing: border-box;
            }
            #fooder {
                background: #cc4a5d;
                height: 100px;
                clear: both;

                /* 添加 10px 的内边距 */
                padding: 10px;
                width: 100%;
                border: 10px solid red;

                /* 利用 box-sizing 进行修改 */
                -moz-box-sizing: border-box;
                -webkit-box-sizing: border-box;
                -o-box-sizing: border-box;
                -ms-box-sizing: border-box;
                box-sizing: border-box;
            }
        </style>
    </head>
    <body>
        <div class="wrapper">
            <div id="header">页眉</div>
            <div id="sidebar">侧边栏</div>
            <div id="content">主内容</div>
            <div id="fooder">页脚</div>
        </div>
       
    </body>

</html>

经过修改之后的结果如图所示: 运行结果_3.png

写在后面的话

这篇文章介绍了CSS中非常重要的一个概念————盒模型。从逻辑抽象到简单布局应用,最后还有一个CSS3为了解决排版问题的新特性。如有错误,还请各位大佬指正,感激不尽。