CSS进阶(5)—— 深入理解margin

  盒尺寸中padding负责内边距,一般情况下(抛开上一章的诡异现象)不会给使用者带来太多的麻烦,因此作者称之为温和的padding,而margin则有些激进,虽说负责外边距,但有时候还能做一些"内边距"的事情(负边距),还自带了些特殊属性(如叠压),本文会通过实例深入探究margin负边距的使用以及叠压问题的产生和计算方式。依旧先放上内容摘要,请按需观看。

1.margin负边距的正确打开方式

2.深入探究margin合并的三个条件(发现书上的内容不全对)

3.margin合并的计算规则

4.深入理解margin:auto,实现上下水平垂直居中

1.margin负边距的正确打开方式

  说到margin,通常我们会想到一层透明的外边距,用于划分元素与元素之间的界限,然而margin除了可以划分边界,还可以改变元素"可视尺寸",注意这里我没有用内部尺寸,因为margin和padding在改变元素可视尺寸方面几乎是互补的。对于设定了width或者元素保持"包裹性"的时候,padding会改变元素的可视尺寸,而margin正好相反,margin只会在元素“充分利用空间”状态的时候,才能改变元素的可视尺寸。当然这两个也不是完全互补的,这里当作一个思考,请自己体会。

  刚才说到了设定了宽度的元素和保持“包裹性”的元素不能通过margin影响可视尺寸,设定width这个很好理解,那么保持包裹性的元素有哪些呢?

  这里来列举一下常见的有:absolute,fixed,float,inline-box(inline-block, table-cell, table-caption, flex, inline-flex)。

  所以我们在遇到上述元素的时候,就不需要尝试用margin去改变他的可视区了,无效。由于我平时最爱用的inline-block元素也在其中,所以我很少用margin负边距去管理元素可视区。下面,我们来看一个最简单的margin负边距影响元素可视区的演示。

<div class="father">
	<div class="son"></div>
</div>
<style>
	.father{
		width: 300px;
		height: 200px;
		background: red;
	}
	.son{
		margin: 0 -20px;
		height: 100px;
		background: yellow;
	}
</style>

  这里有两个要注意的点,首先son是display默认为block的元素,符合充分利用水平空间的规则,其次son自身不带width申明,所以width在负边距的作用下最终width = father.width + 20*2,如上图所示。

  负边距除了能够改变"充分可利用空间"的可视区域之外,还可以利用其可以改变尺寸的特性,实现一些特殊的布局效果。如作者给出的例子如下:

<div class="box box-right-same">
    <div class="full">
        <p>DOM文档流中,图片定宽在右侧,视觉呈现也在右侧,顺便表现此时一致。</p>
    </div>
    <img src="1.jpg" class="img">
</div>
<style>
/* 右浮动,图片DOM在后,和视觉表现一致 */
.box-right-same > .full {
    width: 100%;
    float: left;
}
.box-right-same > .full > p {
    margin-right: 140px;
}
.box-right-same > img {
    float: left;
    margin-left: -128px;
}
</style>

  结果如上图,在本例中,由于full的宽度是100%,且他和img元素均为浮动元素,因此img元素在没有设置margin之前应当流到full元素下面,而加了负边距之后,img元素的宽度增加了128px,正好等于图片的宽度,此时图片元素全部跑到了增加的负边距中去,导致img元素本身变成了"0宽度",于是0宽度元素就浮上来了,因为他“不需要”占据宽度,他的宽度由负边距提供了。(这一段测试个人持保留意见,有不同观点的可在下方留言

2.深入探究margin合并的三个条件

  块级元素的上外边距(margin-top)和下外边距(margin-bottom)有时会发生“重叠”,这样的现象叫做margin合并。从定义上来看,可以确认两个信息。

  (1)块级元素

  (2)垂直方向。不考虑writing-mode的情况下,文档流默认为水平方向,因此这里的垂直方向是指垂直于文档流的方向,而不是简单的上下左右。

  margin合并一般有三种场景。

  (1)相邻兄弟元素margin合并。

  (2)父级和第一个/最后一个子元素。(作者的这个表达可能有一些问题,需要配合第三点来看

  (3)空块级元素的margin合并。

  下面我将列举一些场景,来探究一下每个场景的哪些margin发生了叠压。

<p>一段话</p>
<p>一段话</p>
<p>一段话</p>
<p>一段话</p>
<style>
body{
	margin: 0;
}
p{
	margin: 1em 0;
}
</style>

  

    这个例子中,显然是相邻兄弟元素的margin合并,可以看到p标签的上下外边距是1em,但每两行之间的的距离并不是1+1=2,而是1和1叠压之后 = 1。下面来看第二个场景。

<div class="father">
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	background: green;
	height: 400px;
}
.son{
	margin-top: 200px;
	height: 200px;
	background: red;
}
</style>

  在本例中,父元素高度400,子元素高度200,上外边距200,想象之中,子元素应该"定位"在父容器底部,但由于父级和第一个/最后一个子元素的margin叠压(这个理论是否完全正确我们在后面的例子中证明),子元素的margin-top"借"给了父元素,然后自己的margin-top似乎变成了0,在实际开发的时候,父子元素的margin合并也会给我们带来许多麻烦,那么,如何解决这个烦恼呢?作者提供了几种方案(满足任何一种即可),这里我会有一些自己的观点在里面。

  (1)父元素设置为块状格式化上下文元素(听不懂没关系,overflow:hidden就可以)

  (2)父元素设置border-top/bottom(>0)的值(border-top解决margin-top,border-bottom解决margin-bottom)

  (3)父元素设置padding-top/bottom(>0)的值(同border)

  (4)父元素和第一个子元素之间添加(非空)内联元素进行分隔(针对margin-top)

  (5)父元素和最后一个子元素之间添加(非空)内联元素进行分隔(针对margin-bottom)

  (6)父元素设置height,min-height或max-height(注意本条只对margin-bottom有效)

  经过本人测试,CSS世界似乎对申明这个玩意不感冒,作者说设置border/padding的值即可,我设置0,"竟然"不行,所以补充了>0的限制条件,但由于这条规则破坏了容器的大小,所以不推荐这两种解决方案。

  在实际验证的时候,第四条/第五条在谷歌浏览器中也会由于“0”值不生效,因此我把它划掉了,因为这个解决方案实在是太蠢了,你必须要在内联元素里面写点什么才能解决margin叠压问题,这可比破坏容器大小严重多了,直接就影响文本显示了。因此最佳的解决方案就是第一条,父元素设置为块状格式化上下文元素,虽然我并不知道这个是什么意思。

  进入今天的重头戏,我觉得作者写的有问题的一个点,我们先在刚才父子叠压代码的基础上添加一个空块级标签。

<div class="father">
	<!-- 我是一个空块级元素 -->
	<div></div>
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	background: green;
	height: 400px;
}
.son{
	margin-top: 200px;
	height: 200px;
	background: red;
}
</style>

  此时你会发现页面无任何变化,其实这里涉及到两个知识点,首先是空块级标签的margin叠压,由于其本身没有任何宽高,也没有margin值,因此他只会和相邻的son元素进行叠压,空div的margin-bottom:0 和 son元素的margin-top:200合并之后,可以认为空div的margin-bottom变成200了,此时,空块级元素的margin-bottom:200又和自身的margin-top:0合并,使得自身的margin-top也受到了感染,最后叠压成垂直margin=200的空块级元素,这时候父元素感应到他的第一个子元素有margin-top,就和他进行了一波margin-top叠压,所以最终的表现和第二个例子的结果相同。

  测试到这里,我还觉得没什么问题,然后我又想到刚才内联空标签对第二个例子也不会有任何影响,那么问题来了,父级和第一个/最后一个子元素的叠压这句话究竟是什么意思?惊觉这句父元素和第一个子元素之间添加内联元素进行分隔似乎还有什么别的意思,我个人猜测作者是想表达内联元素破坏了发生叠压的三个规则,因为内联元素不会发生margin叠压,因此可以用这个进行分割(即使该内联元素为空,当然实际测试中为空是没有任何效果的)。根据测试结果,我提出的一个大胆的假设:margin叠压,会直接忽略掉所有空标签(当然空标签不能有什么奇奇怪怪的样式)。这么一来,内联空标签的问题就解决了。当然这个假设还有待验证,去作者提供的论坛碰碰运气。

3.margin合并的计算规则

  关于margin合并的计算规则,我个人倾向于完全套用作者的三句精辟总结:

  “正正取大值”

  “正负值相加”

  “负负最负值”

  这里我只说明正负值相加的情况,虽然这东西其实并没有什么软用,看下面的例子

.a{margin-bottom:50px}
.b{margin-top:-20px}
<div class="a"></div>
<div class="b"></div>

 此时a和b的间距=-20+50 = 30px

4.深入理解margin:auto

  总是喜欢以深入命名,其实就是一个?测试,根据浏览器的表现结果,来猜测原理,在理解margin:auto之前,先来看下面这个例子。

<div class="father">
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	height: 400px;
	background: yellow;
}
.son{
	width: 200px;
	margin-right: 100px;
	margin-left: auto;
	height: 200px;
	background: red;
}
</style>

  可以从结果中得出如下结论:

  (1)margin:auto是有用的,去掉margin-left:auto后,margin-right失效

  (2)margin:auto属性管理的是容器的剩余空间

 何为容器的剩余空间?最寻常的情景就是你在body里添加了一个宽度<body宽度的块元素,此时块元素由于其自占一行的特性,本身会占满这一行,但他的实际宽度又被width给限定了,那么除了width以外的空间就是该块元素的容器剩余空间了。我们最常使用的margin:auto水平居中,就是通过margin管理容器剩余空间,由于margin-left:auto = margin-right:auto,所以这个块元素就水平居中了。如下所示:

  那么问题又出现了,为什么此时在垂直方向上没有垂直居中呢?原因在于触发margin:auto计算有一个前提条件,就是width或height为auto时,元素是具有对应方向的自动填充属性的。从本例来看,当son的width为auto时,son的宽度为100%,也就是可计算的剩余空间为100%-width具体值,当son的height为auto时,不好意思,son的高度变0了,看都看不见了,你还要居中他干啥。同时height:auto也不符合自动填充特性。

  利用margin:auto管理剩余空间的特性,我们除了可以做到元素的水平居中,还可以实现元素的"右浮动"。只需要如下设置即可

<div class="father">
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	height: 400px;
	background: yellow;
}
.son{
	width: 200px;
	margin-left: auto;
	height: 200px;
	background: red;
}
</style>

 这里我们通过margin-left:auto管理剩余空间,元素就自动右对齐了。

 除了水平居中和右对齐,margin:auto还可以实现水平垂直居中。刚才也说到了,要实现垂直居中,只需要让元素在垂直方向上也就是height:auto具有自动填充属性即可。那么什么情况下height:auto有自动填充属性呢?有一种情况就可以,绝对定位元素设置了top和bottom属性之后,元素垂直方向上就会自动填满父容器。如下所示

<div class="father">
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	height: 400px;
	background: yellow;
	position: relative;
}
.son{
	position: absolute;
	width: 200px;
	height: 200px;
	background: red;
	margin: auto;
	left: 0;
	top: 0;
	right: 0;
	bottom: 0;
}
</style>

  

  由于设置了right,left,top,bottom值,子元素在水平垂直方向上都有用自动填充属性,通过margin管理剩余空间,就实现了垂直和水平方向的填充。

  margin如此高深,我真的头疼。

  下一章的内容是border,有兴趣的加个关注吧~

发布了109 篇原创文章 · 获赞 196 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/dkr380205984/article/details/84299612