Blog icon indicating copy to clipboard operation
Blog copied to clipboard

CSS 世界 —— 盒尺寸四大家族

Open shenxuxiang opened this issue 6 years ago • 0 comments

盒尺寸四大家族

替换元素

通过修改某个属性值呈现的内容就可以被替换的元素就被称为 “替换元素”。<img>、<object>、<vedio>、<iframe>、<textarea>、<input> 等都是典型的替换元素

替换元素除了内容可替换以外,还有哪些特性:
  • 内容的外观不受页面上的 CSS 的影响

  • 有自己的尺寸:在不设置尺寸的时候,这些元素会有一个默认的尺寸(300 * 150),如: <video>、<canvas>、<iframe><img> 图片默认展示的就是图片的原始尺寸。

  • 在很多 CSS 属性上有自己的一套表现规则。比较有代表性的就是 vertical-align 属性。对于字符而言,vertical-align 的默认值就是 baseline,也就是字母 “x” 的下边缘。但是,对于替换元素而言就不一样了。对于图片来说 vertical-align 的默认值就是图片的下边缘。

替换元素的尺寸计算规则

可以将替换元素的尺寸从内到外分为:“固有尺寸”、“HTML尺寸”、“CSS尺寸”

  • 固有尺寸:指的就是替换内容原本的尺寸。例如,图片、视频作为一个单独的文件存在的时候,都有着自己的宽度和高度,这个宽度和高度就是“固有尺寸”

  • HTML尺寸:一些替换元素存在 widthheightHTML 属性,例如 <img><canvas> 元素。这个称为 HTML 尺寸

  • CSS尺寸:指的是可以通过 CSS 的 widthheight 或者 max-width/min-widthmax-height/min-height 设置元素的尺寸。这种称为 CSS 尺寸

计算规则
  • 没有 “CSS尺寸” 和 “HTML尺寸” 时,则使用 ”固有尺寸“ 或者 默认尺寸(300 * 150)
  • 没有 ”CSS尺寸“,则使用 “HTML尺寸”
  • 如果有 ”CSS尺寸“,则使用 ”CSS尺寸“

如果 ”固有尺寸“ 含有固有的宽度比例,同时仅设置了宽度或者仅设置了高度,则元素依然按照固有比例宽高显示

padding

  <div style="height: 32px; line-height: 32px; background: #444">
    xxxx<span style="background: #999; padding: 20px 0; border: 5px solid #fff">我是谁</span>xxxx
  </div>
  <div style="height: 32px; line-height: 32px; background: #555">
    xxxx的是非得失丰富的水分是我的  
  </div>

先看看上面这个 demo,可以看出来,对于内联元素来说,paddingborder 对内联元素的文字对齐没有影响。(其实内联元素的 padding/border 不计算在行框盒子的高度内)

注意:padding 不能出现负值

padding 的百分比值
  • 块级元素的 padding 的百分比值无论是水平方向和垂直方向都是相对于宽度进行计算的

  • 内联元素的 padding 的计算规则就有一点点的差异了

    • 同样相对于 宽度进行计算
    • 默认的高度和宽度细节有差异
    • padding 会断行,简单说就是,如果内联元素出现换行时,每一行都会有 padding ,这个地方和块级元素是有差异的。看看下面的这个 demo,就知道了
  <div style="width: 100px; border: 2px dashed #444">
    <span style="background: #999; padding: 50% 0;">我是谁我是谁我是谁我是谁</span>
  </div>
HTML标签内置的 padding
  • <ol>|<ul> 列表内置 padding-left,但是单位不是 px,而是 em。

  • 表单元素内置的 padding

  • <input>、<textarea> 输入框内部内置 padding

  • <button> 按钮内置 padding

  • 部分浏览器 <select> 下拉内置 padding

  • <radio>、<checkbox> 单选框和复选框无内置 padding

同背景属性结合绘制特殊的图形
  <div style="height: 300px; background: #ccc; margin: 20px">
    <div class="circle"></div>
    <div class="three-line"></div>
    <div class="triangle"></div>
  </div>
  .circle {
    width: 20px;
    height: 20px;
    padding: 5px;
    background: #fff;
    background-clip: content-box;
    border: 2px solid #fff;
    border-radius: 50%;
  }

  .three-line {
    width: 20px;
    height: 25px;
    border: 3px solid #fff;
    border-left-width: 0;
    border-right-width: 0;
    padding: 8px 0;
    background: #fff;
    background-clip: content-box;
    box-sizing: border-box;
  }

  .triangle {
    display: inline-block;
    background: transparent;
    border: 10px solid transparent;
    border-top-color: #333;
  }

margin 与元素尺寸以及相关布局

先介绍几个概念术语

  • 元素尺寸:包含 padding、borde、content 部分的尺寸,可以通过 offsetWidth、offsetHeight 获取。也被称为 “元素偏移尺寸”

  • 元素内部尺寸:包含 padding 不包含 border ,也就是元素的 padding box 尺寸。通过 clientWidth、clientHeight 获取。同时也被称为 “元素可视尺寸”(不含滚动条的宽度)

  • 元素外部尺寸:包含 padding、border、margin、content,也就是元素的 margin box 尺寸。

margin 与元素的内部尺寸

当元素设置了 width 属性或者当元素具有 “包裹性” 的时候,margin 对尺寸是没有影响的。只有当元素是 “充分利用可用空间” 的时候,margin 才可以改变元素的可视尺寸。

  <div style="margin-left: 100px;">
    <div style="background: #ccc">123</div>
  </div>

同样,垂直方向也是可以的

  <div style="position: relative; width: 100px; height: 100px; background: #333">
    <div style="background: #ccc; position: absolute; top: 0; bottom: 0; margin-top: 30px;">123</div>
  </div>

上面的 demo 可以看出来,当前元素表现为 “格式化宽高” 的时候,元素自动充满父容器的宽度和高度,这个时候 margin\border\padding\content 自动分配水平和垂直方向空间。当元素表现为正常流特性的时候,和 “格式化宽度” 是一样的。

看看下面的几个 demo

  <div style="background: #ccc; overflow: hidden">
    <img src="./static/images/timg.jpg" alt="" class="right">
    <div class="left">1234567890</div>
  </div>
  .left {
    margin-right: 100px;
  }
  .right {
    float: right;
    width: 100px;
  }
  <div style="background: #ccc; overflow: hidden">
    <img src="./static/images/timg.jpg" alt="" class="left">
    <div class="right">1234567890</div>
  </div>
  .right {
    margin-left: 100px;
  }
  .left {
    float: left;
    width: 100px;
  }
  <div style="background: #ccc; overflow: hidden">
    <div class="middle">
      <div class="con">1234567890</div>
    </div>
    <img src="./static/images/timg.jpg" alt="" class="left">
    <img src="./static/images/timg.jpg" alt="" class="right">
  </div>
  .right {
    float: left;
    width: 100px;
    margin-left: -100px;
  }
  .left {
    float: left;
    width: 100px;
    margin-left: -100%;
  }
  .middle {
    /*margin: 0 100px;*/
    float: left;
    width: 100%;
  }
  .con {
    margin: 0 100px;
  }
  <div style="background: #ccc; overflow: hidden">
    <img src="./static/images/timg.jpg" alt="" class="left">
    <img src="./static/images/timg.jpg" alt="" class="right">
    <div class="middle">
      <div class="con">1234567890</div>
    </div>
  </div>
  .right {
    float: right;
    width: 100px;
  }
  .left {
    float: left;
    width: 100px;
  }
  .middle {
    width: 100%;
  }
  .con {
    margin: 0 100px;
  }

注意: 这个时候当 margin 为负值的时候,容器的 width 会增加

  <div style="margin-right: -30px; background: #333; text-align: right">1234567890</div>
margin 与元素的外部尺寸

在 CSS 世界中很多棘手的问题都是需要借助 margin 的外部尺寸特性来实现的。比方说, FireFox 浏览器下,设置了 overflow: auto/scroll 的容器会忽略 padding-bottom 值。

  <div style="padding: 50px; background: #ccc; height: 200px; overflow: scroll;">
    <img src="./static/images/timg.jpg" alt="" height="300px">
  </div>

注意:如果 padding-bottom 属性的数值设置的过小,则看不出是否被忽略的效果,所以这里我设置的是 50px

那么对于上面的问题,我们可以在最后一个子元素中设置 margin-bootom 来替换就可以了。


margin 和 padding 之间的相互作用

margin 为负值的时候,“元素尺寸” 就会变大。这个时候,如果 padding/border 是固定的数值,那么 content (这里的 width 的值为 auto)就会随着 margin 负值的增加而变大,但对于 height 来说,没有任何影响(不会影响 height 值)。

注意:不是定位方向的 margin 是不会对元素的定位产生影响的,只能对该元素后面相邻的元素定位产生影响。

我们这里有一个需求是:一个盒子内部需要让左右两栏的背景高度一致,取最高那部分的高度。(使用 margin 和 padding 互补)

  <div style="max-height: 200px; overflow: hidden;">
    <div style="float: left; width: 50%;background: #ccc; padding-bottom: 500px; margin-bottom: -500px;">
      <p>1213r</p>
      <p>1213r</p>
      <p>1213r</p>
      <p>1213r</p>
      <p>1213r</p>
      <p>1213r</p>
      <p>1213r</p>
      <p>1213r</p>
    </div>
    <div style="float: left; width: 50%; background: #444; padding-bottom: 500px; margin-bottom: -500px;">
      456
    </div>
  </div>

paddingmargin 互补部分的尺寸是不会计算到布局定位中的

margin 的百分比值 和 padding 一样都是相对于父容器的 width 进行计算的

margin 的合并

块级元素的上、下外边距有时候是会发生合并的,发生 “margin合并” 必须满足两点重要信息

  • 块级元素,但不包含浮动、绝对定位元素
  • 只发生在垂直方向上
哪些场景会发生合并:
  • 相邻兄弟元素 margin 合并

  • 父容器和第一个字元素的 margin 合并(子元素大于1个,发生 margin-top 合并)

  <div style="height: 200px; background: #444">
    <div style="width: 50%; height: 50px; background: #ccc; margin-top: 100px">
    </div>
  </div>

这个时候的 margin 会合并到父元素上,也就是说 margin-top 实际上是作用在父元素上

  • 父容器和最后一个子元素的 margin 合并(子元素大于1个,发生 margin-bottom 合并)
  <div style="background: #444">
    <div style="width: 50%; height: 50px; background: #ccc; margin-bottom: 100px">
    </div>
  </div>

这个时候的 margin 会合并到父元素上,也就是说 margin-bottom 实际上是作用在父元素上。注意:这个时候父元素不能设置 height 属性

  • 父容器只含有一个子元素的时候,且父元素没有设置 height 属性,那么 margin-top、margin-bottom 都可能与父元素发生 “margin合并”。如果设置了 height 属性,那么就不会发生 margin-bottom 的合并。注意,这里的合并最终效果都是作用在了父元素身上。
  <div style="background: #444">
    <div style="width: 50%; height: 50px; background: #ccc; margin: 100px">
    </div>
  </div>
如何消除 margin-top 合并
  • 父元素设置 BFC 块级格式化上下文

  • 父元素设置 border-top

  • 父元素设置 padding-top

  • 在父元素和第一个字元素之间添加一个 内联元素 进行分隔

如何消除 margin-bottom 合并
  • 父元素设置 BFC 块级格式化上下文

  • 父元素设置 height 、min-height 或者 max-height

  • 父元素设置 border-bottom

  • 父元素设置 padding-bottom

  • 在父元素和最后一个字元素之间添加一个 内联元素 进行分隔

如果一个元素是一个空元素(没有任何内容),那么如果同时设置了 margin-topmargin-bottom。那么这个时候 margin-topmargin-bottom 是会发生合并的。所以实际定位时只计算一个 margin-top/margin-bottom 的值(谁的值大取谁)。如何消除这种情况的 margin 合并
  • 设置垂直方向的 padding
  • 设置垂直方向的 border
  • 里面添加内联元素(直接 space 键空格是没用的)
  • 设置 height 或者 min-height
margin 合并的计算规则:
  • 如果元素的 margin 都是正值,那么取最大的那个
  • 如果元素的 margin 一正一负,那么取正负相加后的值
  • 如果元素的 margin 都是负值,那么取最负的那个
margin: auto 作用机制
  • 正常流 ** 对于 margin-left、margin-right,如果一侧定值一侧为 auto,那么 auto 为剩余空间大小
  <div style="width: 100px; margin: 0 0 0 auto; background: #ccc">123</div>

  <div style="width: 100px; margin: 0 auto 0 0; background: #444">123</div>

** 对于 margin-left、margin-right,如果都为 auto,那么 平分剩余空间

  <div style="width: 100px; margin: 0 auto; background: #444">123</div>
  • 格式化宽高

    ** margin-left、margin-right 或者 margin-top、margin-bottom,如果一侧定值一侧为 auto,那么 auto 为剩余空间大小

  <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 200px; height: 200px; margin: 100px auto 0; background: #444">123</div>

  <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 200px; height: 200px; margin: auto auto 0; background: #444">123</div>

** 对于 margin-left、margin-right 或者 margin-top、margin-bottom,如果都为 auto,那么 auto 为剩余空间大小

  <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 200px; height: 200px; margin: 0 auto; background: #444">123</div>
 
  <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 200px; height: 200px; margin: auto auto; background: #444">123</div>

下面的这个案例就可以完美的解释 margin: auto

  <div style="width: 300px; background: #ccc">
    <div style="width: 200px; margin-left: auto; margin-right: 80px; background: #455">123</div>
  </div>
什么情况下设置的 margin 无效
  • display 计算值为 inline 的非替换元素的 margin-top、margin-bottom。内联替换元素的垂直 margin 是有效的,并且没有 margin 合并的问题,所以图片不会发生 “margin合并”

  • 表格中的 <tr>、<td> 元素或者设置 display: table-cell | table-row 的元素的 margin 都是无效的

  • margin 合并的时候,更改 margin 值可能是没有效果的(只有当更改的那个 margin 值大于另外一个 marin 值时才会生效)。

  • 绝对定位元素非定位方位的 margin 值 “无效”。其实并不是 margin 没有生效,实际上,绝对定位元素任意方位的 margin 值无论在什么场景下都是一直有效的,比如下面的 demo 。这种情况,margin-bottom: 80px 其实增加了元素的外部尺寸。所以在元素的底部和父元素的底部间隔 80px。

  <div style="width: 300px; height: 100px; background: #ccc; overflow: auto; position: relative;">
    <div style="position: absolute; top: 0; left: 0; height: 100px; margin-top: 100px; margin-bottom: 80px; background: #455">123</div>
  </div>
  • 正常流水平方向上的 margin-right 或者 margin-bottom 都是 “无效”。对元素的定位是不起作用的。但是这种情况和上面的 demo 一样,会增加元素的外部尺寸。
  <div style="width: 300px; background: #ccc; overflow: auto;">
    <div style="height: 100px; margin-top: 100px; margin-bottom: 80px; background: #455">123</div>
  </div>

这种情况也可以该改变。给元素设置 float: right 后,那么这个时候 right 就成了定位方向了,这个时候 margin-right 就起作用了。

border

border-width
  • thin - 薄薄的,等于1px;

  • medium - 薄厚均匀,等于3px;默认值。

  • thick - 厚厚的,等于4px

border-style 默认值就是 none。所以当我们设置 border-widthborder-color 没有显示边框的原因。但是当我们只设置 border-style: solid 就会出现边框。
  • none - 无,默认值

  • solid - 实线边框

  • dashed - 虚线边框

  • dotted - 虚点边框

  • doubble - 双线边框:当 border-width 小于 3px 的时候,展示的只有一条边框线。

  • inset - 内凹槽

  • outset - 外凸槽

  • groove - 沟槽

  • ridge - 山脊

border-color 有一个很重要的特性就是 border-color 的默认值就是 color 色值。当没有设置 border-color 颜色值的时候,会使用当前元素的 `color 计算值作为边框色。

shenxuxiang avatar Oct 28 '19 09:10 shenxuxiang