老生常谈的盒模型

101 阅读3分钟

前言

盒模型都知道:
content-box:width = 内容宽度
border-box:width = 内容宽度 + padding + border
但如果有滚动条(bar)时又如何呢?

Chrome

content-box:

     Cwidth
       ||
     Twidth padding bar border margin
       |             |
       ---  width  ---

width = Twidth(图上蓝框width) + bar的width
同时Twidth = Cwidth(计算后的样式width)
也就是说,计算Cwidth时,chrome会减去bar的width

  • clientWidth= padding外边缘之间的距离(... Twidth + padding)
  • scrollHeight= padding外边缘之间的距离(Twidth由内容完全撑开时,b的width<Twidth)
  • offsetWidth= border外边缘之间的距离(... Twidth + padding + bar + border)
  • getBoundingClientRect().width= border外边缘之间的距离

border-box:

     Twidth padding bar border margin
       |      |      |    |       
       -----   width  -----
                ||
              Cwidth

width = Twidth + padding + bar + border
这时width = Cwidth
注意Twidth并不是图中右下方盒子所标注的80,会被bar侵蚀一部分

  • clientWidth= padding外边缘之间的距离(... Twidth + padding)
  • scrollHeight= padding外边缘之间的距离(Twidth由内容完全撑开时,b的width为80>Twidth)
  • offsetWidth= border外边缘之间的距离(... Twidth + padding + bar + border)
  • getBoundingClientRect().width= border外边缘之间的距离

Firefox

而在Firefox中,bar不参与到盒模型中
content-box:

     Cwidth
       ||
     Twidth padding border margin
       ||          
      width

width = Twidth(图上浅蓝框width)
同时Twidth = Cwidth(计算后的样式width)

  • clientWidth= padding外边缘之间的距离(... Twidth + padding)
  • scrollHeight= padding外边缘之间的距离(Twidth由内容完全撑开时)
  • offsetWidth= border外边缘之间的距离(... Twidth + padding + border)
  • getBoundingClientRect().width= border外边缘之间的距离

border-box:

     Twidth padding border margin
       |      |       |       
       ---  width ----
             ||
           Cwidth

width = Twidth + padding + border
这时width = Cwidth
注意Twidth就为图中右下方盒子所标注,不会被bar侵蚀

  • clientWidth= padding外边缘之间的距离(... Twidth + padding)
  • scrollHeight= padding外边缘之间的距离(Twidth由内容完全撑开时)
  • offsetWidth= border外边缘之间的距离(... Twidth + padding + border)
  • getBoundingClientRect().width= border外边缘之间的距离

offsetParent

  • 标准流:找有定位的祖先元素,直到body
  • float:找有定位的祖先元素,直到body
  • absoute:找有定位的祖先元素,直到body
  • fixed:firefox为body,其他为null
  • body:null

offsetTopfixed为top值。body为0。
其他offsetParent为body时:firefox上元素的border外边缘到offsetParent的border内边缘,其他浏览器为元素的border外边缘到body的border外边缘,所以body不要设border margin
其他offsetParent非body时:都为元素的border外边缘到offsetParent的border内边缘

盒模型与position

  • relative
  • absolute:找有定位的祖先元素,直到document的视口,定位基于padding外边缘(... Twidth + padding)
  • fixed:相对于document的当前视口,定位基于padding外边缘(... Twidth + padding)

盒模型与百分比height

  • 标准流:找父元素,且父元素要设height,为父元素Theight的%
  • float:找父元素,且父元素要设height,为父元素Theight的%
  • absoute:找有定位的祖先元素,祖先元素要是设height,为祖先元素padding外边缘的%,直到document视口的padding外边缘的%(... Twidth + padding)
  • fixed:document当前视口的padding外边缘的%(... Twidth + padding)
  • 标准流html height:100%:document初始视口(ICB)的padding外边缘的%(... Twidth + padding),注意document的滚动条
  • 标准流body height:100%:找html,且html要设height,为htmlTheight的%,注意body的滚动条

特殊点

  • 内联元素的clientWidth clientHeight scrollWidth scrollHeight为0
  • window.innerHeight包括document的滚动条
  • document.documentElement.clientHeight是ICB的高度,也就是初始视口的高度,所以html不要设padding border margin

以上都是基于Twidth所说,Twidth是渲染引擎最终所得的盒子内容,会受其中子元素脱离文档流的影响,包括:

  • 子元素float
  • 子元素absolute、fixed

也就是说,子元素一旦脱标,会对父元素的Twidth造成影响,进而影响父元素的各种大小,更进一步可能影响子元素的百分比大小和position定位。