mengtuifrontend / Blog

芦叶满汀洲,寒沙带浅流。二十年重过南楼。柳下系船犹未稳,能几日,又中秋。 黄鹤断矶头,故人今在否?旧江山浑是新愁。欲买桂花同载酒,终不似,少年游。
18 stars 5 forks source link

CSS世界——内联元素和流 #27

Open shenxuxiang opened 5 years ago

shenxuxiang commented 5 years ago

内联元素和流

字母 x 和 CSS 世界的基线

CSS 规定,小写的字母 x 的下边缘 就是基线(baseline)。字母 x 的高度就是 x-heightx-height 就是术语描述的 "基线" 和 "等分线" 之间的距离。

内联元素的基石 line-height

对于非替换元素的纯内联元素来说,line-height 就是计算高度的基石,更准确一点的说就是:指定了用来计算行框盒子高度的基础。内联盒子的 padding 、border 不包含在行框盒子高度内(这一点很重要)

  <div class="container">
    <span style="padding: 10px; line-height: 32px;">我是谁x</span>
    <img src="./static/img/timg.jpg" alt="" style="height: 20px; vertical-align: top">
  </div>
  .container {
    height: 100px;
    font-size: 24px;
    background: #ccc;
  }

行距和半行距

行距 = lineheight - fontsize。 有了行距,在一分为二就是 “半行距”,分别加在 "em-box" 上面和下面就构成了文字的完整高度了

尺寸标注时,半行距何时向上取整、何时向下取整??

由于文字具有明显的下沉现象,且 font-size 值越大下沉越明显。所以当尺寸标注的是距离文字上边距的话就,半行距就向下取整。否则就向上取整。假如设计师标注了文字上边距距离图片下边缘的距离是 20px,文字的 line-height: 21px, font-size: 14px 那么这个时候 半行距:(21 - 14)/ 2 = 3.5 再向下取整所以这个时候半行距为 3px。

大多数情况下,内容区域和 em-box 是不一样的,内容区域高度受 font-familyfont-size 的双重影响,而 em-box 只受到 font-size 影响,通常内容区域高度会高一些。 当字体是 “simsun” 的时候,内容区域和 em-box 是等高的。因为 “simsun” 字体是等宽等高的。这里我们可以理解为在 Firefix 浏览器下,文本选中的背景颜色区域就是 “内容区域”

  <div class="test">
    <span>spinx</span>
  </div>
  .test {
    font-family: simsun;
    font-size: 24px;
    line-height: 36px;
    background: yellow;
  }
  .test span {
    background: white;
  }

图文混合场景

实际开发中图文混合的场景很多,那么这种内联替换元素和内联非替换元素在一起时候的高度表现如何呢??

  <div style="border: 1px solid #ddd">
    <img src="./static/img/timg.jpg" alt="" height="100px">
  </div>

思考:为什么父容器底部和 img 底部由间隙呢??

这是因为 “幽灵空白节点” 在作祟。默认情况下 <img>baseline 就是自己的下边缘,和文字的 baseline 对齐。所以多出来的距离就是 “幽灵空白节点” 的 baseline 到内容区域底部的距离。

  <div style="border: 1px solid #ddd; line-height: 50px;">
    xxxxs<img src="./static/img/timg.jpg" alt="" height="100px">
  </div>

思考:这个时候图片和文字如何呈现??父元素的高度是多少??

从上面的 demo 中,我们可以看出来,父元素的行高要大于自身设置的 line-height。那么这个时候 line-height 就不能决定容器高度了(只有当设置的行高大于任何子元素的高度时,line-height 才会起到决定容器高度的作用)。

line-height 属性值

元素内置的 line-height

HTML 中的很多替换元素,尤其是表单类的替换元素,如输入框、按钮之类的,很多具有继承特性的 CSS 属性其自己也有一套,如 font-family、font-size、font-weight、font 以及 line-height 。由于继承的权重是最弱的,所以 body 中设置的 line-height 是无法影响到这些替换元素的,但是 * 作为一个选择器,就不一样了,直接会重置这些替换元素默认的 line-height。但是如果考虑到性能或者其他问题,一般情况下很少使用 通配符选择器。

  body {
      line-height: 1.5;
  }
  input, button {
      line-height: inherit;
  }
内联元素 line-height 的 “大值特性”
  <div style="line-height: 50px; background: #444; width: 200px">
    <span style="line-height: 20px">dhfhsfjh解放军互粉可好尽快发货的康师傅尽快发货的复活节岛上</span>
  </div>

  <div style="line-height: 20px; background: #444; width: 200px">
    <span style="line-height: 50px">dhfhsfjh解放军互粉可好尽快发货的康师傅尽快发货的复活节岛上</span>
  </div>

看看上面的两个 demo 有没有什么区别?? 其实上面的两个 demo 没有啥区别,样式都是一样的。这是为什么呢?? 因为 "行框盒子" 的高度是由这一行最高的那个内联元素决定的,第一个 demo 中,<span> 和 “幽灵空白节点” 组合为一个行框盒子,高度由 “幽灵空白节点” 的高度决定,因为父元素的 line-height<span>line-height 要大。 第二个 demo 和第一个一样,只不过这个时候 "行框盒子" 的高度是由 <span>line-height 决定的。所以两个 demo 的呈现样式都是一样的。

vertical-align

  <div style="line-height: 32px;">
      <span style="font-size: 24px;">都是废话话费放胡椒粉</span>
  </div>

思考:父元素表现出来的高度是多少??注意:这里需要考略 vertical-align 和字体下沉。

vertical-algin 的属性值

上面的 “数值、百分比” 应该是两类,分别是 “数值类” 和 “百分比类”。放在一起是因为两者有很多的共性。根据计算值的不同相对于基线往上或往下偏移。当为正值时往上移动,为负值时往下偏移。

  <div style="border: 1px solid #ddd; line-height: 50px;">
    xxxxs<img src="./static/img/timg.jpg" alt="" style=“height: 100px; vertical-align: 10px”>
  </div>

注意: 当 vertical-align: 0 时,其实和 vertical-align: baseline 是一样的。

vertical-align 百分比值 相对于 line-height 进行计算的。line-height 的百分比值则相对于 font-size 进行计算的。

vertical-algin 起作用的条件

只能适用于 内联元素以及 displaytable-cell、inline-block、inline-table、table-cell 的元素上。

看下面这个 demo ,我们想让这个图片垂直居中显示

  <div class="mid">
    <img src="./static/img/timg.jpg" alt="" style="height: 100px; vertical-align: middle;">
  </div>
  .mid {
    height: 200px;
    border: 1px solid #ddd;
  }

但是,最终显示的效果却没有达到我们的预期。为什么??

因为容器的高度是 200,图片的高度时 100,正常的 CSS 布局是默认左上方排列对齐,所以图片的顶部和父容器的顶部对齐。 再者就是 <img> 此时是一个内联元素,所以在它的前面会有一个 “幽灵空白节点”。在这一行构成一个行框盒子, 当给 <img> 元素设置 vertical-align: middle 后,希望图片的垂直中心点和 "行框盒子" 的基线往上二分之一处 x-heighter 对齐。我们可以在 <img> 前面添加一个字母x,方便我们观察。 这个时候的 "行框盒子" 的 baseline 在什么地方?? 就是图片的下边缘,又因为当前字符 x 的高度很小,所以当前 "行框盒子" 的高度是由图片的高度决定的(由 height 最大的那个内联盒子决定)。 所以这个时候图片是不会发生偏移的,实际偏移的是字符 x。所以才会出现 vertical-align: middle 无效的情况

我们加入一些代码,做个修改。通过下面的修改后,就ok了。达到我们的目的了。这又是为什么???

  .mid:after {
    content: '';
    display: inline-block;
    height: 100%;
    vertical-align: middle;
  }

为什么会这样呢?其实还是因为行框盒子的高度影响到。在元素中添加了一个为元素,并设置了它的高度就是容器的高度。这个时候 after 就和 <img> 、“幽灵空白节点” 一起组成了当前行的 "行框盒子" 。所以当前行框盒子的高度就是 after 高度,其实这个时候字符 x 和 after 还有图片都是底部对齐的(不设置 vertical-align: middle 的情况下)。所以 after 需要设置 vertical-align: middle,这样字符 x 就垂直居中了。 这个时候 <img> 设置了 vertical-align: middle 同样 <img> 的垂直中心点和字符x往上二分之一处 x-heighter 对齐,所以这个时候 图片就居中了。

vertical-alignfont-size 的关系

因为文字具有下沉的现象,所以当 font-size 越大的时候 文字下沉的越明显。默认情况下,文字都是基线对齐的,所以当字号大小不一样的两个文字在一起的时候,彼此就会发生上下位移。

  <div class="demo1">
    <span style="background: #ccc">xxxxx</span>
    <span style="background: #444; font-size: 24px;">xxxxx</span>
  </div>
  .demo1 {
    margin: 30px;
    line-height: 100px;
    background: #ddd;
    position: relative;
  }
  .demo1:after {
    content: '';
    position: absolute;
    top: 0;
    left: 50px;
    width: 5px;
    height: 50%;
    background: #f80;
  }

当第二个 <span> 元素的 font-size 逐渐变大时,会发现两个元素会发生上下位移。父元素中设置了 line-height: 100px。所以两个元素的高度就是100px;当两个元素发生错位的时候 高度就会变大。那么我们的父元素的高度就随着变大了。

我们来看下面这个demo,为什么容器底部有间隙?

  <div class="demo">
    <span style=" background: #444">xsdhg</span>
    <img src="./img/timg.jpg" alt="" style="height: 100px;">
  </div>
  .demo {
    font-family: simsun;
    font-size: 20px;
    margin: 30px;
    line-height: 1;
    border: 1px solid #ddd;
  }

间隙的大小就等于字体的 baseline 到字体下边缘的距离。 因为 line-height 存在继承,即使我们没有设置 line-height,他仍然存在。 准确的来说,字符 x 的高度其实要小于 font-size。所以当我们将 line-height 设置为 1 的时候,这个间隙刚好就是文字体基线到字体下边缘的距离了,这个距离我们定义为 x。line-height: 1 的时候其实是没有行距的,或者说行距为0,如果这个时候 line-height: 2,那么半行距刚好等于 0.5 个 font-size 的大小,这个距离定义为 y。所以这个时候间隙的大小就等于 x + y。

如何消除这种行为:
在 CSS 世界中,非主动触发位移的内联元素是不可能跑到计算容器外面的,原因是图片的位置被 “幽灵空白节点” 的 vertical: baseline 给限定死了。
  <div class="demo3">
    x<img src="./static/img/timg.jpg" alt="" style="height: 100px; margin-top: -100px">
  </div>
  .demo3 {
    border: 1px solid #ddd;
  }

inline-blockbaseline

我们现在知道了 baseline 就是文本之类的内联元素字符 x 的下边缘,对于替换元素就是替换元素的下边缘。但是,如果是 inline-block 元素,则规则就要复杂了。

如果元素内部没有内联元素,或者设置了 overflow(不能是 visible),则该元素的 baseline 就是元素的 margin-bottom 的下边缘。否则就是元素内最后一行内联元素的基线

  <div class="demo4">
    xxx
    <div class="demo4-con">的废话废话废话少放胡椒粉和客户反馈复活节肯定很舒服</div>
  </div>
  .demo4 {
    width: 300px;
    line-height: 120px;
    border: 1px solid #ddd;
  }
  .demo4-con {
    width: 200px;
    overflow: auto;
    margin-bottom: 10px;
    display: inline-block;
    line-height: 20px;
    background: #ccc;
  }

vertical-align: top / bottom

元素顶部和当前行框盒子的顶部 / 底部 对齐

  <div class="demo5">
    x
    <img src="./static/img/timg.jpg" alt="" style="height: 80px; vertical-align: top">
    <img src="./static/img/timg.jpg" alt="" style="height: 80px; vertical-align: bottom">
  </div>
  .demo5 {
    height: 100px;
    line-height: 100px;
    border: 1px solid #ddd;
  }

vertical-align: middle

元素的垂直中心点和行框盒子基线往上 二分之一 x-height 处对齐

vertical-align: text-top / text-bottom

  <div class="demo6">
    xgh
    <img src="./img/timg.jpg" alt="" style="height: 80px; vertical-align: text-top">
    <img src="./img/timg.jpg" alt="" style="height: 80px; vertical-align: text-bottom">
  </div>
  .demo6 {
    height: 100px;
    line-height: 100px;
    font-size: 32px;
    border: 1px solid #ddd;
  }

最后我们通过 vertical-align 实现一个水平垂直居中

  <div class="container">
    <div class="dialog"></div>
  </div>
  .container {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    font-size: 0;
    text-align: center;
    background: rgba(0,0,0,.6);
    white-space: nowrap;
    overflow: hidden;
  }
  .container:after {
    content: 'x';
    display: inline-block;
    height: 100%;
    width: 0px;
    vertical-align: middle;
  }
  .dialog {
    display: inline-block;
    vertical-align: middle;
    width: 200px;
    height: 200px;
    background: #fff;
  }