深入CSS盒模型

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44196299/article/details/98870313

一、外在盒子和内在盒子

在张鑫旭老师的《CSS世界》中提出每个元素都有两个盒子(注意此处的盒子并不是CSS盒模型,而是对元素结构与内容相关的一种形象说法):外在盒子和内在盒子(专业名称是“容器盒子”)。外在盒子是决定元素排列方式的盒子,即决定盒子具有块级特性还是内联特性的盒子。外在盒子负责结构布局。内在盒子是决定元素内部一些属性是否生效的盒子。内在盒子负责内容显示。
例如 display: inline-table; 外在盒子就是inline,内在盒子就是table。外在盒子决定了元素要像内联元素一样并排在一排显示,内在盒子则决定了元素可以设置宽高、垂直方向的margin等属性。不过有以下两种情况比较特殊:

  • li元素默认的display值是list-item,是块级元素,list-item元素会出现项目符号是因为生成了一个附加的盒子,学名叫“标记盒子(marker box)”,专门用来放圆点、数字这些项目符号。IE浏览器下伪元素不支持list-item或许就是无法创建这个“标记盒子”导致的
  • display值为block的元素的盒子由外在的“块级盒子”和内在的“块级容器盒子”组成,值为inline-block的元素则由外在的“内联盒子”和内在的“块级容器盒子”组成,值为inline的元素则内外均是“内联盒子”

“内外盒子”的观点在很大程度上能够帮助我们理解CSS中的一些概念及页面场景:

  • 内容区域:本质上是字符盒子。在浏览器中,文字选中状态的背景色就是内容区域。
  • 内联盒子:内联盒子就是指元素的外在盒子是内联的,会和其他内联盒子排成一行。
  • 行框盒子:由内联元素组成的每一行都是一个行框盒子。如果一行里面没有内联元素如一个空的div标签,则不会形成行框盒子。行框盒子由一个个内联盒子组成,如果换行,那就是两个行框盒子。比如一个包含了很多字符的换行的的p标签,每一行都存在一个行框盒子。值得注意的是,如果给元素设置display: inline-block,则创建了一个独立的行框盒子。line-height是作用在行框盒子上的,并最终决定高度。
  • 包含盒子:就是包含块。多行文字组成一个包含块,一个包含块有若干个行框盒子,包含盒子也就是元素进行定位和计算的参考元素。
  • 幽灵空白节点:内联元素的每个行框盒子前面有一个“空白节点”,这个“空白节点”不占据任何宽度,无法选中获取,但是又实实在在存在,表现就如同文本节点一样。

二、盒模型

盒模型在MDN中的解释为:

在一个文档中,每个元素都被表示为一个矩形的盒子。确定这些盒子的尺寸, 属性 — 像它的颜色,背景,边框方面 —和位置是渲染引擎的目标。
在CSS中,使用标准盒模型描述这些矩形盒子中的每一个。这个模型描述了元素所占空间的内容。每个盒子有四个边:外边距边,,边框边,内填充边与内容边。

emmm,官方的语言果然一般听不明白,下图为Chrome控制台盒模型的截图:
在这里插入图片描述
如上图所示,元素的内在盒子是由margin box、border box、padding box、content box组成的,这四个盒子由外到内构成了盒模型。盒模型有两种,一种是W3C的标准盒模型,还有一种是IE盒模型,两者之间的主要区别是在两种模型中宽(width)和高(height)属性的不同,下面我们分别对两种模型进行介绍:
1、WSC标准盒模型
在这里插入图片描述
在W3C标准盒模型中:

  • CSS中的宽(width)=内容(content)的宽
  • CSS中的高(height)=内容(content)的高

2、IE盒子模型
在这里插入图片描述
IE盒子模型中:

  • CSS中的宽(width)=内容(content)的宽 +(border+padding)*2
  • CSS中的高(height)=内容(content)的高 +(border+padding)*2

示例:

#box {
	width: 50px;
	height: 50px;
	padding: 2px;
	border: 1px;
	margin: 3px;
}
<div id="box"></div>

如果为W3C标准盒模型:
div的实际大小:
        div高=height+(padding+border+margin)*2=50+(2+1+3)*2=62px;
        div宽=width+(padding+border+margin)*2=50+(2+1+3)*2=62px;
div内容(content)占大小:
        div高=height=50px;
        div宽=width=50px;

如果为IE盒模型:
div的实际大小:
        div高=height+margin2=50+32=56px;
        div宽=width+margin2=50+32=62px;
div内容占大小:
        div高=height-(border+padding)*2=50-(1+2)*2=44px;
        div宽=width-(border+padding)*2=50-(1+2)*2=44px;

这两种不同的盒模型往往导致我们的代码运行在不同的浏览器会产生不一样的页面效果,那么我们有没有什么办法能够实现在不同浏览器上使得元素的盒模型保持一致呢?
答:通过在代码顶部加如下的 doctype 声明,可以使页面元素在各个浏览器中都以W3C盒子模型渲染。

<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">

除此以外我们还可以通过box-sizing属性实现盒模型的切换,box-sizing属性用来定义渲染引擎如何计算一个元素的总宽度和总高度,可选值如下:

  • content-box (默认值)是W3C盒子模型, width 与 height 只包括内容的宽和高, 不包括边框。
  • border-box 是IE盒子模型, width 和 height 属性包括内容,内边距和边框,但不包括外边距。但要注意,填充和边框将在盒子内 , 例如, .box {width: 350px; border: 10px solid black;} 导致在浏览器中呈现的宽度为350px的盒子。内容框不能为负,并且被分配到0,使得不可能使用border-box使元素消失。

示例:

<style>
	div {
		width: 160px;
		height: 80px;
		padding: 20px;
		border: 8px solid red;
		background: yellow;
		margin: 5px;
	}
	#contentBox {
		box-sizing: content-box;
	}
	#borderBox {
		box-sizing: border-box;
	}
</style>
<body>
	<div id="contentBox">contentBox</div>
	<div id="borderBox">borderBox</div>
</body>

页面效果:
在这里插入图片描述
contentBox的盒模型结构图如下所示:
在这里插入图片描述
borderBox的盒模型结构图如下所示:
在这里插入图片描述

三、content、border、padding和margin

1、content
CSS中的content属性主要用于伪元素before、after中生成内容,这一特性目前已被所有主流浏览器支持,在CSS 3 Generated Content工作草案中,content属性添加了更多的特征,例如:插入以及移除文档内容的能力,以创建脚注,结语,及段落注释。但是目前还没有浏览器支持content的扩展功能。
关于content属性还需要注意:

  • 对于非替换元素如div,其content就是div内部的元素。
  • 对于替换元素,其content就是可替换部分的内容。

大家看到这儿肯定会疑惑什么是替换元素,什么是非替换元素?
关于替换元素W3C中给出的定义为:

An element that is outside the scope of the CSS formatter , such as an image, embeded document, or applet.

从定义中我们了解到,替换元素就是有CSS格式化外表范围的元素,也可以将其理解为content box可以替换的元素,即存在src=""属性的img、 audio、 video、 iframe元素和可以输入文本的input、 select、textarea元素等。进而可知非替换元素就是除了替换元素img、input等元素以外的元素。
所有替换元素都是内联元素,默认display属性是inline或inline-block(除了input[type=“hidden”]默认display: none;)。
替换元素有自己默认的样式、尺寸(根据浏览器不同而不同),而且其vertical-align属性默认是bottom(非替换元素默认值是baseline)。

2、border
border 简写属性在一个声明设置所有的边框属性。
可以按顺序设置如下属性:

  • border-width
  • border-style
  • border-color

如果不设置其中的某个值,也不会出问题,比如 border:solid #ff0000; 也是允许的。
(1)border-width:
border-width属性用于指定边框宽度,默认值是3px,这个取值主要是为了方便border-style中的double(双边框),border-width属性的取值除了直接指定宽度值,还可以使用三个关键字,它们分别是thin 、medium(默认值) 和 thick。
注意:CSS 没有定义 3 个关键字的具体宽度,所以一个用户代理可能把 thin 、medium 和 thick 分别设置为等于 5px、3px 和 2px,而另一个用户代理则分别设置为 3px、2px 和 1px。
(2)border-style:
border-style属性用于设置元素所有边框的样式,可选值如下:

  • none:定义无边框。
  • hidden:与 “none” 相同。不过应用于表时除外,对于表,hidden 用于解决边框冲突。
  • dotted:定义点状边框。在大多数浏览器中呈现为实线。
  • dashed:定义虚线。在大多数浏览器中呈现为实线。
  • solid:定义实线。
  • double:定义双线。双线的宽度等于 border-width 的值。
  • groove:定义 3D 凹槽边框。其效果取决于 border-color 的值。
  • ridge:定义 3D 垄状边框。其效果取决于 border-color 的值。
  • inset:定义 3D inset 边框。其效果取决于 border-color 的值。
  • outset:定义 3D outset 边框。其效果取决于 border-color 的值。
  • inherit:规定应该从父元素继承边框样式。

(3)border-color:
border-color属性用于定义边框颜色,默认跟随字体颜色,取值可以使用任何类型的颜色值,例如可以是命名颜色,也可以是十六进制和 RGB 值,在有的情况下人们希望能够与创建一个透明边框,因此CSS2中引入了边框颜色值transparent(IE7+),从某种意义上说,利用 transparent,使用边框就像是额外的内边距一样;此外还有一个好处,就是能在你需要的时候使其可见。这种透明边框相当于内边距,因为元素的背景会延伸到边框区域(如果有可见背景的话),并且transparent属性在利用border属性图形构建的时候很重要。

border属性的另一个重要作用就是图形构建:
示例:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8"/>
 	<title>图形构建</title>
 	<style type="text/css">
 		#container {
   			border: 2px dashed gray;
  		}
  		#first {
   			width: 0px;
   			border: 40px solid;
   			border-color: transparent transparent red transparent;
   			margin: 10px;
   			display: inline-block;
     		}
  		#second {
   			width: 0px;
   			border: 40px solid;
   			border-color: transparent transparent blue blue;
   			margin: 10px;
   			display: inline-block;
  		}
  		#third {
   			width: 40px;
   			height: 40px;
   			border: 20px solid;
   			border-color: red yellow green blue;
   			margin:10px;
   			display: inline-block;
  		}
 	</style>  
</head>
<body>
	<div id="container">
  		<div id="first"></div>
  		<div id="second"></div>
  		<div id="third"></div>
 	</div>
<body>
</html>

页面效果:
在这里插入图片描述
3、padding
padding 简写属性在一个声明中设置所有内边距属性。关于padding属性我们需要注意以下几点:

  • 行内非替换元素上设置的内边距不会影响行高计算;因此,如果一个元素既有内边距又有背景,从视觉上看可能会延伸到其他行,有可能还会与其他内容重叠。(在IE标准盒模型中,由于width属性和height属性的计算是包含padding,这让很多人以为在IE盒模型中元素行高line-height也包括padding,实际上line-height属性是不作用于padding属性的)
  • padding不可为负值,padding属性的取值有auto、以具体单位计的内边距数值、基于父元素的宽度的百分比和inherit。为百分比时水平和垂直方向的padding都是相对于父级元素宽度计算的。
  • padding配合background-clip属性,可以制作一些特殊形状

示例:

<style>
	#first {
		box-sizing: border-box;
  		display: inline-block;
  		width: 20px;
  		height: 10px;
  		padding: 5px 0;
  		border-top: 5px solid currentColor;
  		/*currentColor是css中为数不多的变量,指当前文字的颜色值,非常好用*/
  		border-bottom: 5px solid currentColor;
  		background: currentColor;
  		/*注意如果此处背景颜色属性用缩写的话,需要放到其他背景属性的前面,否则会覆盖前面的属性值(此处为background-clip)为默认值*/
  		background-clip: content-box;
  		margin-right: 10px;
  	}
  	#second {
  		display: inline-block;
  		width: 12px;
  		height: 12px;
  		padding: 6px;
  		border: 6px solid currentColor;
  		border-radius: 50%;
  		background-color: currentColor;
  		background-clip: content-box;
  	}
<style>
<body>
	<div id="first"></div>
	<div id="second"></div>
</body>

页面效果:
在这里插入图片描述
4、margin
margin简写属性在一个声明中设置所有外边距属性。该属性可以有 1 到 4 个值,关于margin属性我们需要注意以下几点:
(1)块级元素的垂直相邻外边距会合并,而行内元素实际上不占上下外边距。行内元素的的左右外边距不会合并。块级元素在垂直方向发生margin合并,存在以下三种场景:

  • 相邻兄弟元素之间margin合并;
  • 父元素margin-top和子元素margin-top,父元素margin-bottom和子元素margin-bottom;
  • 空块级元素自身的margin-top和margin-botom合并;

要阻止margin合并,有以下四种措施:

  1. 把元素放到bfc中;
  2. 设置border或padding阻隔margin;
  3. 用内联元素(如文字)阻隔;
  4. 给父元素设定高度。

(2)作为外边距,margin属性并不会参与盒子宽度的计算,但通过设置margin为负值,却能改变元素水平方向的尺寸。
示例:

<style>
	#container {
		 width: 400px;
	}
	#box {
		margin: 0 -100px;
	}
</style>
<body>
	<div id="container">
		<div id="box"></div>
	</div>
</body>

此时div元素的宽度是比父级元素的宽度大200px的。但是这种情况只会发生在元素是标准流布局的时候,即元素width是默认的auto并且可以撑满一行的时候。如果元素设定了宽度,或者元素设置了float: left / position: absolute这样的属性改变了流体布局,那么margin为负也无法改变元素的宽度了。
(3)浮动元素的外边距也不会合并。允许指定负的外边距值,不过使用时要小心。
(4)margin取值可以为百分比,这点跟padding一样,垂直方向的margin和水平方向上的一样都是相对于父元素宽度计算的。
(5)margin属性几乎可以作用于所有元素,除了表格显示类型的元素(不包括table-caption、table和inline-table),而且垂直外边距对非替换内联元素不起作用。
(6)margin: auto能在块级元素设定宽高之后自动填充剩余宽高。margin: auto自动填充触发的前提条件是元素在对应的水平或垂直方向具有自动填充特性,显然默认情况下块级元素的高度是不具备这个条件的。典型应用是利用margin来实现块级元素的水平居中:

div {
	width: 200px;
	margin: 0 auto;
}

auto的特性是,如果两侧都是auto,则两侧均分剩余宽度,这也是能够用margin实现水平居中的原理,如果一侧margin是固定的,另一侧是auto,则这一侧auto为剩余宽度。
除了水平方向,垂直方向的margin也能实现垂直居中,但是需要元素在垂直方向具有自动填充特性,而这个特性可以利用position实现:
示例:

<style>
	#container {
		width: 400px;
		height: 400px;
		border: 2px solid gray;
		position: relative;
	}
	#box {
		position: absolute;
		left: 0; right: 0; top: 0; bottom: 0;
		width: 200px;
		height: 200px;
		margin: auto;
	}
</style>
<body>
	<div id="container">
		<div id="box"></div>
	</div>
</body>

页面效果:
在这里插入图片描述
(7)大家往往会好奇margin属性是以何为基准来促使盒模型的产生呢?即margin属性的取值将以何为基准进行移动?我们不妨把这个基准称为参考线,实际上margin中top、bottom、left和right的参考线并不一致,而是分为了两类,实际上top和left的参考线属于一类,right和bottom的参考线属于一类,top以父元素的content上边或垂直上方相连元素的margin的下边为参考线垂直下移,left以父元素的content左边或水平左方相连元素的margin的右边为参考线垂直右移;right以元素本身border右边为参考线右移,bottom以元素本身的border下边为参考线垂直下移。从上我们可以看出top和left都是以外元素为参考,而right和bottom都是以自身为参考。(如果margin取值为负则移动方向恰好相反)
示例:

<style>
	#first {
		width: 200px;
		height: 200px;
		background: #ccc;
		margin: -10px 20px -30px 40px;
	}
	#second {
		width: 200px;
		height: 200px;
		background: yellow;
	}
</style>
<body>
	<div id="first"></div>
	<div id="second"></div>
</body>

页面效果:
在这里插入图片描述
如下图所示,我们可以清楚看到margin属性修改后的first元素所占标准文档流的位置大小
在这里插入图片描述
用margin最后的显示大小到底是怎么样的,或许有朋友也比较疑惑,我暂时用逻辑大小和物理大小来区分,到底什么是逻辑大小,什么是物理大小呢?!具体可以看图,物理大小指的是除去margin,也就是包含border以内的box大小,而逻辑大小,则是box通过margin解析规则解析后得到的大小(这或许可以解释为什么IE5会错误解析盒模型),当逻辑大小小于物理大小时,则不会影响实际box的显示,也就是说,此时显示的是box的物理大小,而当逻辑大小大于物理大小时,则此时显示逻辑大小。这仅对元素本身有效,对于其他相关元素,他们则只以margin的逻辑大小为准则,进行布局。

四、JavaScript获取盒模型宽高

通过JavaScript获取盒模型对应的宽和高,有以下几种方法:
1、 Element.style.width/height
  这种方式只能取到dom元素内联样式所设置的宽高,也就是说如果该节点的样式是在style标签中或外联的CSS文件中设置的话,通过这种方法是获取不到dom的宽高的。
2、 Element.currentStyle.width/height
  这种方式获取的是在页面渲染完成后dom节点的实际宽高,就是说不管是哪种方式设置的样式,都能获取到。
  但这种方式只有IE浏览器支持。
3、window.getComputedStyle(Element).width/height
  这种方式的原理和2是一样的,这个可以兼容更多的浏览器,通用性好一些。
4、Element.getBoundingClientRect().width/height
  这种方式是根据元素在视窗中的绝对位置来获取宽高的,适用场所:计算一个元素的绝对位置,相对于视窗左上角左顶点位置;可以获取到dom四个参数:left、top、width、height。

猜你喜欢

转载自blog.csdn.net/weixin_44196299/article/details/98870313
今日推荐