1. 文档流(Normal Flow)
    1. 内联元素的宽高
    2. 块级元素的宽高
    3. 水平居中
    4. 垂直居中
    5. 文字溢出省略(多行)
  2. 盒模型
    1. 一比一的 div
    2. outline
    3. border 调试大法

说在前面的话

在页面中不应该经常写死宽高

文档流(Normal Flow)

文档流关键词:块级元素从上到下独占一行,内联元素从左到右宽度不够自动换行。就算为块级元素设置宽度,块级元素也是独占一行的。

文字间对齐方式

在各种内联相关模型中,凡是涉及到垂直方向的排版或者对齐的,都离不开最最基本的基线(baseline)。英文小写字母x的下边缘(线)就是父容器的基线。例如,line-height行高的定义就是两基线的间距vertical-align的默认值就是基线;vertical-align是定义 inline 元素的垂直对齐方式的,因此 inline 元素默认是沿着父容器的基线对齐的 。看下图

宽度和高度 - 图1

可以很明显看出文字和文字之间是通过基线(base-line)对齐的,而不是中线对齐。code

给 div 设置 font-size: 24px,div 内的文字高度不一定是 24px ,还是要看浏览器最终渲染出来的高度,还跟 font-family 有关。

  1. // <div class="div-1">我们</div>
  2. // <div class="div-2">的爱</div>
  3. .div-1 {
  4. font-size: 24px;
  5. font-family: Microsoft YaHei;
  6. }
  7. .div-2 {
  8. font-size: 24px;
  9. font-family: "Helvetica Neue", Helvetica, Arial;
  10. }

默认行高(line-height)是字体设计师写在字体文件里的,她觉得那样好看,不同字体(font-family)默认行高不一样。

关于 line-height

比如我们可以给 div 设置行高 (line-height),那么这个 div 在浏览器渲染出来以后的height就是 line-height 的值。

举例:下面是HTML

<div class="div-1">我们</div>

设置CSS

* {margin: 0;padding: 0;box-sizing: border-box;}
.div-1 {
  font-size: 24px;
  font-family: Microsoft YaHei;
}

此时 div 在我电脑上渲染出来的高度是 31.3333px

现在设置 line-height

.div-1 {
  font-size: 24px;
  font-family: Microsoft YaHei;
  line-height: 32px;
  overflow: hidden;
  background: pink;
}

我们设置了 大于之前渲染出的 div 高度的 line-height。为了效果更明显,加了 overflow 和 背景。然后在谷歌控制台-查看div 元素,一点点减小 line-height 的数值,从 32px 减到 0 ,你会发现 页面上什么也没有了,查看整个文档的高度也是 0 ,这个现象说明 div 的 高度 与 font-size 值没有半毛钱关系, div 的 高度 仿佛由 line-height 决定。

为了进一步验证,我们将文档的 font-size 设为 0 px。

/* <div class="div-1">我们</div> */
* {margin: 0;padding: 0;box-sizing: border-box;}
html {
  font-size: 0px;
}
.div-1 {
  /* font-size: 24px; */
  font-family: Microsoft YaHei;
  line-height: 32px;
  overflow: hidden;
  background: pink;
}

我们发现此时 div 的 最终渲染高度 还是 32px。

但是呢,div 的 高度 并不完全由 line-height 决定。当 div 内容 为空时,div 的 最终渲染高度 是 0px

/* <div class="div-1"></div> */
* {margin: 0;padding: 0;box-sizing: border-box;}

.div-1 {
  font-size: 24px;
  font-family: Microsoft YaHei;
  line-height: 32px;
  overflow: hidden;
  background: pink;
}

上面代码中 ,div 的 最终渲染高度 是 0px。

div 的高度 由什么决定?

因此,我认为 div 的高度是由其内部文档流中元素的高度的总和决定的,注意不会计入脱离文档流元素的高度

  • 当 div 容器 有单行行内内容时,div 的高度值 就是 line-height 值,若没有设置 line-height,那么高度不确定,反正不是 font-size 值。
  • 当 div 容器 有多行行内内容时,不要设置 line-height 属性, div 的高度值 是多行行高的和,文档流自适应的。由于英文单词不可分隔性,浏览器默认不会给一个因为单词断开,哪怕它很长很长。因此可以加分隔符- 或者 word-break: break-all;,这样浏览器就会自动断开单词了。code
  • 当 div.dad 内有 div.child 时 ,div.dad 的内容的高度就是 div.child 内容高度 + padding + border + margin,但是有时候会出现 div.dad 和 div.child 的 margin 合并 ,下面复现一下,codehtml <div class="dad"> <div class="son">111</div> </div> ```css
  • {margin: 0;padding: 0;box-sizing: border-box;} body { border: 1px solid; } .dad { / border: 1px solid; / outline: 1px solid green; / 标识元素作用 / } .son { border: 25px solid red; padding: 10px; margin: 40px; } <br />outline 是用来描绘元素的轮廓,它与 border 属性不同的是,outline **不占据空间**,outline 被描绘在内容之上,也就是outline 并不会增加元素高宽,但是同时也就意味着 `outline`也没有形成元素和元素之间的阻挡屏障,因为`outline`是浮在内容之上的,本质上就起着**标识元素位置**或者**样式**的作用。在 chrome 查看元素,发现 outline 并不会<br />上面代码中 margin 合并的现象就是,div.son 的 上下 margin 并没有按照文档流 成为 div.dad 的内容,而是溢出了 div.dad ,被 body 元素拦截了,作为 body 的内容,为什么可以被 body 拦截呢,是因为我们设置了 body 的 border 属性。现在尝试把 body 的 boder 属性去掉,你会发现 div.son 的 上下 margin 会溢出 div.dad 和 body ,但被文档的根元素 html 拦截,作为 html 的内容,而且 html 也不用设置 border。[代码](http://js.jirengu.com/pepip/2/edit)如下css
  • {margin: 0;padding: 0;box-sizing: border-box;} html { border: 1px solid blue; } body { / border: 1px solid; / } .dad { / border: 1px solid; / outline: 1px solid green; text-align: center; } .son { border: 25px solid red; padding: 10px; margin: 50px; outline: 25px solid blue; } ```
示意图

宽度和高度 - 图2

如何避免 margin 合并?

给父元素加 border 或者 padding 阻隔一下,而给 父元素加 margin 的话,结果是父元素的 上下margin会和 子元素的 上下 margin 合并,取两者之间的大的margin 值,左右 margin 不会合并。因此 如果 给子元素设置 上下margin 是你必须要做的事,那么请记得给 父元素加上 padding 或者 border ,否则产生margin 合并,样式不如意。code

div 的宽度由什么决定的?

我们都知道 div 是独占一行的,那么独占一行的 width 是多少呢?

首先 div 的宽度分两种,div 可视区域的宽度(也叫元素的CSS宽度,div.clientWidth) 和 div元素总宽度(包括溢出容器的不可见部分的宽度,div.scrollWidth)。所有元素都是这么分的,谷歌控制台盒模型显示的只是元素可视区域宽高

div 可视区域的宽度 div.clientWidth

我们给 元素设置的 width属性,是控制元素的可见区域的宽度。

在没有给 div 设置宽度的情况下,div 可视区域的宽度 始终等于 body的可见区域宽度(body的可见区域宽度不包括纵向滚动条宽度的),div 总宽度 始终等于 body的总宽度。文档页面的可视区域的宽度 就是 body的可见区域宽度。也就是:

var div = document.querySelector('div')

// 可视区域的宽度
document.body.clientWidth === div.clientWidth
或
window.getComputedStyle(div).width === window.getComputedStyle(document.body).width

// 元素总宽度
document.body.scrollWidth === div.scrollWidth

当我们缩放浏览器窗口时,是可能会出现横纵滚动条的。由于视口宽度===文档页面的可视区域的宽度(body的可见区域宽度)+纵滚动条宽度,因此缩放浏览器窗口时,视口宽度(window.innerWidth)改变,文档页面的可视区域的宽度(body的可见区域宽度)做响应式改变,那么div 可视区域的宽度也做响应式改变。

总之,只要不单独给 div 设置 width,div 可视区域的宽度 就跟着 文档页面的可视区域的宽度(body的可见区域宽度)变

图示

宽度和高度 - 图3

由图容易得

// 视口的宽度 
window.innerWidth === document.body.clientWidth + 纵滚动条的宽度

注意,window.innerWidth、element.clientWidth 和 window.getComputedStyle(div).width获取的都是对象可视区域的宽度

假如给 body设置宽度(max-width\min-width\width),这个 width 也是控制 body 的可视区域的宽度

// body { width: 600px; }
var div4 = document.querySelector('.div-4')
console.log("div4的宽度" + div.clientWidth) //  600
console.log("文档页面的宽度" + document.body.clientWidth) // 600

以上内容总 code

div 总宽度 div.scrollWidth

首先明确 元素总宽度(element.scrollWidth),是元素内容总宽度(包括溢出元素容器、当前不可见的部分)、padding和伪元素的宽度,但是不包括bordermargin、水平滚动条宽度。

 div.scrollWidth === document.body.scrollWidth

我们知道 div 内的文本从左到右排列,且文本内容宽度超过 div 父容器宽度 会自动换行,换行时注意英文单词具有不可分割性。

假设我们让div 内的所有文本单行显示(div {white-space: nowrap}),这样文本内容太长就会溢出 div 父容器,此时若是拖动浏览器使得 div 的 可视区域 小于 文本内容总长度,也就是div的可视区域 小于文档页面总宽度,就可以看到页面中出现了水平滚动条。此时文档页面总宽度其实就是由 文本内容总长度撑起来的,此时给 body 加 背景色,会发现并不只是可视区域背景色变了,超出 body 容器的部分背景色也变了,说明background-color 是应用于 文档页面总宽高的。chrome 控制台不能选择查看溢出容器的部分。此时我们可以通过 document.body.scrollWidth读取当前文档页面的总高度。

但是一般我们不希望出现水平滚动条,根据用户习惯,大家看网页都是上下滑动,可以有垂直滚动条。因此我们要隐藏水平方向溢出容器的内容(设置 overflow: hidden),就涉及到了单行文本省略和多行文本省略的问题。

单行文本省略

<div>
  这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的文本。
</div>
* {margin: 0;padding: 0;box-sizing: border-box;}
div {
  white-space: nowrap; /* 不让多行文本换行显示,也就是文本只能单行显示*/
  overflow: hidden;   /* 超出容器部分就隐藏 */
  text-overflow: ellipsis; /* 超出容器部分的文本以省略号显示 */
}

单行文本省略就是上面三句话,如此一来就能消灭水平滚动条了。codedemo,记得视口放小一点,才能看到省略。

overflow: hidden;是只要超出容器就给你隐藏掉,包括水平的和垂直两个方向的,因为我们让文本单行显示了,当将视口放小一点存在水平方向的溢出问题,而 div 的 高度是由 div 内的行内内容的 line-height 撑起来的,因此不存在垂直方向的溢出问题。

多行文本省略

谷歌:css multi line ellipsis

 <div>
    这是一个很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的文本
  </div>
div {
  display: -webkit-box;
 -webkit-box-orient: vertical;
 -webkit-line-clamp: 3; /*行数*/
  overflow: hidden;
}

上面这种方法只是适用于 webkit ,手机所有浏览器都支持,只有 IE 和 firefox 不支持 。code

浏览器市场份额,使用 ff 多为开发者,使用 QQ浏览器的都比 ff 多。firefox 和 safari 不到 3% 。不用兼容IE低版本,希望IE早日退出市场。

单行文本的两端对齐:不要用空格做布局

比如我们想实现两个字和四个字对齐,如下面这种效果

姓    名
联系方式

下面是错误的示范

 <div>
    姓&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;名<br>
    联系方式
  </div>

&nbsp;是 no break space ,空格的宽度是设计师决定的。

em 也是与字体设计有关。

正确示范

 <div>
    <span>姓名</span><br>
    <span>联系方式</span>
 </div>
div {
  font-size: 20px;
}
span {
  display: inline-block;
  width: 4em;
  text-align: justify; /* 多行文本两端对齐,单行文本不生效 */
  /* background: pink; */
  height: 1em;
  line-height: 1em;
  overflow: hidden;
}
span::after {  /* 伪装一个span有两行文本*/
  content: '';
  display: inline-block;
  width: 100%;
  /* border: 1px solid; */
}

上面这个代码要理解原理并背下来,以后遇到可以借鉴。

text-align CSS属性定义行内内容(例如文字)如何相对它的块父元素对齐。

text-align: justify;文字向两侧对齐,对最后一行无效,即如果是单行文本就没有作用。因此用伪元素假装 span 是有两行的,span::after是作为 span 内容的一部分的,因此必须要想办法隐藏 span::after

效果图 code

宽度和高度 - 图4

inline inline-block 元素之间默认的空格

只要是 inline 或 inline-block 元素 之间有 回车\tab\空格\任何看不见的字符,浏览器就会默认渲染成一个空格。这也就是行内元素默认有间隙。不要用 inline-block,用了元素间就有间隙。比如下面这种情况:

<div>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>
  </div>

不要使用 display: inline-block 做 导航栏,会有默认间隙。用 float,而且 不要用 overflow: hidden;清除浮动,用 clearfix 。

错误示范

*{margin: 0;padding:0;box-sizing: border-box;}
ul,li {list-style: none;}
div {
  border: 1px solid;
}
li {
  display: inline-block;
  border: 1px solid red;;
}

正确方式用 float + clearfix。code

*{margin: 0;padding:0;box-sizing: border-box;}
ul,li {list-style: none;}
div {
  border: 1px solid;
}
ul::after {
  content: '';
  display: block;
  clear: both;
}
li {
  float: left;
  border: 1px solid red;
}

垂直居中

div 的垂直居中分两种情况,实现内联文本垂直居中,实现块级元素垂直居中

  • 第一种是 子元素是内联文本( Text 节点)或内联元素(同常是span),一般要么就是一行文本或者多行文本,要么就是一个或者多个 span ,不要在一个 div 里面混用 两者。例如不要写成下面这种
<div>
你<span>好</span>啊
</div>

要么这样写

<div>
你好啊
</div>

要么这样写

<div>
<span>你</span><span>好</span><span>啊</span>
</div>
  • 第二种是子元素是块级元素 (div.son),具体方法在下面介绍

文本垂直居中

垂直居中呢,父容器不直接设死宽度,加 padding 就能垂直居中啊。父容器设死宽度,做垂直居中就有点复杂,当然你也可以直接 flex 布局,不过只是一行文本有点大材小用。

技巧:div的 height 可以用 line-height 和 padding 来撑,这样写更灵活美观

  • 当 div 父容器内有单行文本时,如何实现单行文本垂直居中
<div>看着你我知道了天使的模样</div>

1 若div 父容器不设任何高度 (line-height、height都没有),直接给div 父容器 添加 上下 padding 增加自身高度来达到目标高度,就能实现单行文本垂直居中

* {margin: 0;padding: 0;box-sizing: border-box;}
div {
  font-size: 20px;
  background: pink;
  padding: 17px 0;
}

2 若div 父容器高度写死 ( height 设置为目标高度),那么再给div 父容器设置 line-height 值等于 height,就能实现单行文本垂直居中,此时可以只设置 line-height 为目标高度值,可以去掉高度。但是写死高度不好就是 只能实现单行文本垂直居中,一旦 div 内有多行文本就会出现 bug

* {margin: 0;padding: 0;box-sizing: border-box;}
div {
  font-size: 20px;
  background: pink;
  height: 60px; /*可省略*/
  line-height: 60px;
}

上面代码使用 flex 布局 有点大材小用。

* {margin: 0;padding: 0;box-sizing: border-box;}
div {
  font-size: 20px;
  background: pink;

  height: 60px;
  display: flex;
  align-items: center;
}
  • 当 div 父容器内有多行文本时,如何实现多行文本垂直居中

这种情况下,一定不能给 div 父容器 直接写死高度,而是用添加 上下 padding 增加自身高度来达到目标高度

 <div>这是很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长文本</div>

错误示范

* {margin: 0;padding: 0;box-sizing: border-box;}
div {
  font-size: 20px;
  background: pink;
  height: 150px;
  line-height: 150px;
}

正确 code

* {margin: 0;padding: 0;box-sizing: border-box;}
div {
  font-size: 20px;
  background: pink;
  padding: 8px 0;
}
  • 当 div 父容器内有单行多个内联子元素(标签可以相同也可以不同)时,如何实现 这些内联子元素在 div 容器内垂直居中。

1 若div 父容器不设任何高度 (line-height、height都没有),所有子inline 元素全加 vertical-align: middle;,父元素通过添加 上下 padding 增加自身高度来达到目标高度,这样 div 容器内的 inline 元素都是垂直居中的。

vertical-align: middle;作用是使 inline 元素中部与父元素 (这里是 div元素)的基线加上父元素x-height(译注:x高度)的一半对齐,简单说,vertical-align: middle并不是绝对的垂直居中对齐vertical-align: middle只是近似地inline 元素的中部与父元素 (这里是 div元素)的中部对齐,有时候我都能看出来细微差别。

<div class="div-1">
    <span>
      我们
    </span>
     <span>
      的爱
    </span>
    <a href="#">链接</a>
    <i>font</i>
    <big>大的</big>
    <small>小的</small>
  </div>
* {margin: 0;padding: 0;box-sizing: border-box;}
.div-1 {
  font-size: 20px;
  background: pink;
}
span,i,small {
  background: green;
  vertical-align: middle;
}
a,big {
  background: blue;
  vertical-align: middle;
}

宽度和高度 - 图5

现在 div 父容器的渲染高度是 31.604px ,假设 要求div 父容器高度为 60px ,那么 给 div 上下来 4 px 的 padding

.div-1 {
  font-size: 20px;
  background: pink;
  padding: 14px 0;
}

宽度和高度 - 图6

2 若div 父容器高度写死 ( height 设置为目标高度),那么再给div 父容器设置 line-height 值等于 height ,所有子inline 元素全加 vertical-align: middle;,这样 div 容器内的 inline 元素都是垂直居中的。其实设置了 line-height ,height 可以去掉了,但是发现去掉后 div 渲染出来的 height 居然 不是 60px ,是 62.6px,还是留着把。

// 还是上面的HTML

* {margin: 0;padding: 0;box-sizing: border-box;}
.div-1 {
  font-size: 20px;
  background: pink;
  height: 60px;
  line-height: 60px;
}
span,i,small {
  background: green;
  vertical-align: middle;
}
a,big {
  background: blue;
  vertical-align: middle;
}

效果图

块级子元素垂直居中

有7种方法,在另一篇博客,包括了兼容IE的。但是现在的前端不要再去兼容IE,只要记住最快的方式实现垂直居中就行了。

万能的 flex 居中法

display: flex;
justify-content: center;
align-items: center;

span 的 宽度和高度由什么决定的?

span 的 宽度

由 span 内部文字的个数 决定的,span 被渲染出来的宽度 === 字数 * font-size。若给 span 设置 左右 padding、border、margin 都会算在正常文档流中,也就是会增加 span 父元素(祖先元素)的宽度

<body>
  <div><span>宽度由文字数量决定的</span></div>
</body>
div {
  font-size: 20px;
  display: inline-block;
}

在 chrome 查看元素我们会发现盒模型显示 span 宽高为 auto * auto ,但是可以通过父元素来查看 span 被渲染出来的宽高为 200px ,那是因为 span 内有10个文字。code

span 的 高度

内联元素的高度由文字的行高决定。

给 span 设置 上下 padding、border、margin 都不属于正常文档流的一部分,是不占文档空间的,也就是不会增加 span 父元素(祖先元素)的高度。

设置 span 的样式

  • 给 span 设置 宽高属性不生效
  • 给 span 设置 左右 padding、border、margin 都会算在正常文档流中,也就是会增加 span 父元素(祖先元素)的宽度
  • 但是 给 span 设置 上下 padding、border、margin 都不属于正常文档流的一部分,是不占文档空间的,浮在文档内容之上的。也就是不会增加 span 父元素(祖先元素)的高度。因此给 span 设置 上下 padding、border、margin 可能会导致上下行内容覆盖。如下图

宽度和高度 - 图7

当设置 span 为 box-sizing: border-box;,再设置背景颜色和 border 时,是管 上下 padding 的。

code

常见的脱离文档流的方式

某个元素脱离文档流的意思是计算该元素的父元素(甚至祖先)的高度时不要算上该元素的高度,就连文档树的顶部根元素 html的高度都不会计入脱离文档流元素的高度,body 的 最终渲染高度也不会计入脱离文档流元素的高度。也就是脱离文档流的元素实际上是不属于这个文档的,不占空间的,浮在内容之上,就像 元素的 outline 属性一样

以下是常见的三种脱离文档流的方式

  • 元素设置 float 属性
  • 元素在文档中采用绝对定位方式: position: absolute;, position: fixed;

设置了position: absolute;, position: fixed; 属性的元素就会脱离文档流,不属于这个文档,但是浮动于这个文档内容之上,可以使用 top/bottom/left/right偏移属性和 z-index属性进行精确定位,说明是有可能遮挡文档中的内容的,开发过程中一般我们都放在空白处,比如做悬浮窗,hover 一个按钮弹出菜单选项。code

  • position: absolute;

不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置,如果一直沿着文档树向上找,找到 body 元素都不是非 static 定位方式,那么就相对视口(或者说是根元素 html )定位。

  • position: fixed;

不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。

元素在文档中采用正常布局方式(默认的position: static;),和 相对定位方式(position: relative;) 时,该元素是不会脱离文档流的。这两种定位方式元素都不会影响其他元素在文档中的位置

  • position: static;意为元素在文档常规流中当前的布局位置,此时 top, right, bottom, leftz-index属性无效。
  • position: relative;,意为元素先放置在未添加定位时的位置,再在不改变页面布局的前提下根据偏移属性和 z-index 属性 调整元素位置(因此会在此元素未添加定位时所在位置留下空白)。code

默认脱离文档流

  • div 的上下 margin 可能会脱离文档流,可以阻止
  • span 的 上下 padding、border、margin 一定会脱离文档流

实现 1:1的 div

一句话: padding-top: 100%;

// <div class="one"></div>
* {margin: 0;padding: 0;box-sizing: border-box;}
.one {
  padding-top: 100%; /*作用是padding-top和 width 一样高,而 div 的宽度是自适应的*/
  background: pink;
}

完整 code

单独看看 padding border

三角形