重学css(4)深入理解margin

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

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: #F56C6C;
}
.son{
    margin: 0 -20px;
    height: 100px;
    background: #E6A23C;
}
</style>

由于markdown编辑器支持标签语言,因此我们可以直接预览最终效果如下(小提示:你可以通过浏览器直接检查下面的元素看到CSS样式)

这里有两个要注意的点,首先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>
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>

结果如下图所示:

image.png

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

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

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

何为容器的剩余空间?最寻常的情景就是你在body里添加了一个宽度

image.png

那么问题又出现了,为什么此时在垂直方向上没有垂直居中呢?原因在于触发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部分的内容就写到这。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,629评论 1 92
  • 谈到margin就不得不提起到css盒模型,盒模型为外边距+边框+内边距+内容区域,这里我们不谈IE下的怪异模式。...
    Bennt阅读 1,172评论 2 4
  • 作者:[zhengkonghong](https://coding.net/u/zhengkenghong) 本文...
    CODING_博客阅读 754评论 1 6
  • 一、CSS入门 1、css选择器 选择器的作用是“用于确定(选定)要进行样式设定的标签(元素)”。 有若干种形式的...
    宠辱不惊丶岁月静好阅读 1,540评论 0 6
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,104评论 1 45