【百度前端学院学习笔记】Day5 盒子模型
内容来自于:
菜鸟教程
MDN盒模型
注:这一节MDN的中文翻译质量不佳。
一、什么是盒子?
所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用。
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
盒模型允许我们在其它元素和周围元素边框之间的空间放置元素。
下面的图片说明了盒子模型(Box Model):
- Margin(外边距) - 清除边框外的区域,外边距是透明的。
- Border(边框) - 围绕在内边距和内容外的边框。
- Padding(内边距) - 清除内容周围的区域,内边距是透明的。
- Content(内容) -盒子的内容,显示文本和图像。
1.1 标准盒模型
当指定一个 CSS 元素的 宽度(Width) 和 高度属性(Height) 时,你只是设置 内容(Content) 区域的宽度和高度。要知道,完整大小的元素,你还必须添加内边距,边框和边距。
例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<style>
div{border: 2px dashed black;}
div.test {
background-color: lightgrey;
width: 300px;
border: 25px solid green;
padding: 25px;
margin: 25px;
}
</style>
</head>
<body>
<h2>盒子模型演示</h2>
<p>CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。</p>
<div>
<div class="test">这里是盒子内的实际内容。有 25px 内间距,25px 外间距、25px 绿色边框。</div>
</div>
</body>
</html>
呈现的效果如下:
计算<div class = "test">
这个元素的宽度:
300px (width)
+50px (左 + 右padding)
+50px (左 + 右border)
+50px (左 + 右margin)
= 450px
【注意】:margin 不计入实际大小 —— 当然,它会影响盒子在页面所占空间,但是影响的是盒子外部空间。盒子的范围到边框为止 —— 不会延伸到margin。
1.2 替代盒模型(IE风格)
一旦为页面设置了恰当的 DTD,大多数浏览器都会按照上面的图示来呈现内容。然而 IE 5 和 6 的呈现却是不正确的。根据 W3C 的规范,元素内容占据的空间是由 width 属性设置的,而内容周围的 padding 和 border 值是另外计算的。不幸的是,IE5.X 和 6 在怪异模式中使用自己的非标准模型。这些浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度(注意没有外边框margin的宽度)的总和。 这种模式就是替代盒模型。
- 标准盒模型(standard box model):
- 替代盒模型(alternative box model):
默认浏览器会使用标准模型。如果需要使用替代模型,可以通过为其设置 box-sizing: border-box
来实现。 这样就可以告诉浏览器使用 border-box 来定义区域,从而设定您想要的大小。
.box {
box-sizing: border-box;
}
或者设置整个网页采用替代模型,并设置所有元素继承这个属性。
html { //注:html5是所有元素的父元素
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
Internet Explorer默认使用替代盒模型,没有可用的机制来切换。(IE8+ 支持使用box-sizing 进行切换 )
虽然有方法解决这个问题。但是目前最好的解决方案是回避这个问题。也就是,不要给元素添加具有指定宽度的内边距 (padding) ,而是尝试将内边距或外边距添加到元素的父元素和子元素。
IE8 及更早IE版本不支持设置填充的宽度和边框的宽度属性。
解决IE8及更早版本不兼容问题可以在HTML页面声明<!DOCTYPE html>
即可。
二、盒子的应用
2.1 block、inline和inline-block:
就像html5里有块元素和行内元素一样,css3里的盒子也有block
和inline
两种属性。这两种盒子会在**页面流(page flow)**和元素之间的关系方面表现出不同的行为:
1.display=block
一个被定义成 块级的(block),即 display = block
盒子会表现出以下行为:
- 盒子会在水平方向上扩展并占据父容器水平方向的所有可用空间,在绝大数情况下意味着盒子会和父容器一样宽;
- 每个盒子都会换行;
- width 和 height 属性可以发挥作用;
- 内边距(padding), 外边距(margin) 和 边框(border) 会将其他元素从当前盒子周围“推开”
除非特殊指定,诸如标题(<h1>
等)和段落(<p>
)这样的块元素默认情况下都是块级的盒子。
2.display=block
如果一个盒子对外显示为 行内(inline),那么他的行为如下:
- 盒子不会产生换行;
- width 和 height 属性将不起作用;(因为一个行内元素的width默认靠字距,height默认靠行距)
- 垂直方向的内边距(padding)、外边距(margin)以及边框(border)会被应用但是不会把其他处于 inline 状态的盒子推开;(因为说到底它被视作行内盒子,只能在水平方向影响别的元素)
- 水平方向的内边距(padding)、外边距(margin)以及边框(border)会被应用而且也会把其他处于 inline 状态的盒子推开。(行内盒子当然可以在水平方向影响别的元素)
例如:
<p>
I am a paragraph and this is a <span>span</span> inside that paragraph. A span is an inline element and so does not respect width and height.
</p>
span {
margin: 20px;
padding: 15px;
width: 800000px;
height: 500000px;
background-color: lightblue;
border: 2px solid blue;
}
显示的效果如下:
可以看到,width
和height
这两个非常夸张的属性被忽略了,因为段落本来就有自己的字距和行距嘛。
width: 800000px;
height: 500000px;
margin
,padding
和border
则生效了,但是他们只推开了水平方向上下文"inside…",竖向的行距还是一样的,这导致了边框和文字的重合。(这里还有个很有趣的东西:<span>
这个元素只遮挡了上面的行,没有遮挡下面的行。)
用做链接的<a>
元素、 <span>
、 <em>
以及 <strong>
这样的行内元素都是默认处于 inline 状态的。
3.display: inline-block
display有一个特殊的值,它在内联和块之间提供了一个中间状态。这对于以下情况非常有用:我不希望一个项切换到新行,但希望它可以设定宽度和高度,并避免上面看到的重叠。
一个元素使用 display: inline-block
,实现我们需要的块级的部分效果:
- 设置width 和height 属性会生效。
- padding, margin, 以及border 会推开其他元素。
上面的例子稍作修改,改变display
属性的值,
span {
margin: 20px;
padding: 20px;
width: 80px;
height: 50px;
background-color: lightblue;
border: 2px solid blue;
display: inline-block;
}
将会产生如下效果:
可以看到,在保留不换行的基础上,<span>
元素在水平和竖直方向上都推开了周围的文字,并且width
和height
属性得到了应用。
另一个display: inline-block
的应用如下:
一个导航栏的元素<a>
由于是行内元素,通过padding
属性使它扩大之后会导致覆盖了边框:
<nav>
<ul class="links-list">
<li><a href="">Link one</a></li>
<li><a href="">Link two</a></li>
<li><a href="">Link three</a></li>
</ul>
</nav>
.links-list a {
background-color: rgb(179,57,81);
color: #fff;
text-decoration: none;
padding: 1em 2em;
}
.links-list a:hover {
background-color: rgb(66, 28, 40);
color: #fff;
}
使用.links-list a
选择器将display: inline-block
添加到样式规则中,即可通过内边距推开其他元素来修复这个问题。
2.2 改变内部样式布局:display: flex
上述的block
,inline
和inline-block
是元素对外的显示模式,而元素内部也有显示的模式,它决定了子元素是按照正常文档流(normal flow) 还是按照 弹性盒子(Flexbox) 布局。
下面三个html元素,都有同样的外部显示类型 block。
<p>I am a paragraph. A short one.</p>
<ul>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
</ul>
<p>I am another paragraph. Some of the <span class="block">words</span> have been wrapped in a <span>span element</span>.</p>
p,
ul {
border: 2px solid rebeccapurple;
padding: .5em;
.block,
li {
border: 2px solid blue;
padding: .5em;
}
ul {
display: flex;
list-style: none;
}
.block {
display: block;
}
}
第一个是一个段落,在 CSS 中加了边框。浏览器把它渲染成一个块级盒子,所以段落从新的一行开始,而且宽度占满一行。
第二个是一个列表,布局属性是 display: flex。 将在容器中建立一个flex布局,可以看到虽然每个<li>
是块元素,但是依然没有换行,这是因为它不是按照正常文档流(normal flow) 而是按照 弹性盒子(Flexbox) 布局。
第三个是个块级段落,里面有两个 元素。正常情况下是 inline,但是其中一个加了block类,设置属性 display: block。
2.3 外边距折叠(Margin collapsing)
如果你有两个外边距相接的元素,这些外边距将合并为一个外边距,即最大的单个外边距的大小。
这其实不难理解,相邻的两个元素都想通过margin把对方推开,肯定是手长的人(margin大) 决定了二者的距离。
<div class="container">
<p class="one">I am paragraph one.</p>
<p class="two">I am paragraph two.</p>
</div>
.one {
margin-bottom: 50px;
}
.two {
margin-top: 1px;//手短,即使只想把对方推开1px,奈何对方想把你推开50px
}
但是当margin出现负值的时候,表示靠近,这个时候手长的人也**“来者不拒”**了。
.one {
margin-bottom: 50px;
}
.two {
margin-top: -50px; //下面的元素主动往上凑,这时候两个元素的边框正好紧贴着,所以显得很厚。
}