美文网首页前端技术Dev_Web
浅析让人D疼的margin折叠

浅析让人D疼的margin折叠

作者: thisDong | 来源:发表于2018-04-21 14:26 被阅读71次

    margin基础

    在DIV+CSS布局网页的时候,margin是很常用的一个属性。作用是设置元素的外边距。不要问我什么叫外边距的问题,你可以想象一下如果心中的女神突然出现在自己面前,我想在第一时间除了脸红、心跳嘴巴干之外,不会有别的反应。这个时候你敢不敢去牵她的手?如果你说你敢那我只能会以膜拜的神情。对于这种优先下半身思考的模式直接献上我的膝盖了。

    说远了,继续继续~ 这个时候你肯定和她身体有一定距离,这个就叫边距。随着双方理解和信任的增加,她对你牵手kiss之类的举动已经不再抗拒,这个时候你的手或者嘴与对方亲密接触的地方边距就为零,很好理解吧。什么?你说边距为负是什么情况?嘿嘿嘿~ 自己想!

    咳咳~ 我们继续……由盒模型能看出来margin有四个方向margin-topmargin-rightmargin-bottommargin-left。既是分别设置上方外边距、右边外边距、下方外边距、左边的外边距。例如:margin-right:10px;就是设置上方外边距是10个像素。呵呵,顺嘴提一下,margin是可以设置负值的。

    我喜欢margin因为它是可以简写的属性,操作起来特别省代码。具体用法 margin:5px 10px 15px 2px;四个值的排列顺序是上、右、下、左。具体代码解释是分别设置了上边距5px、右边距10px、下边距15px、左边距2px

    根据情况不同,简写的方式也不同。例如上下边距相等都为20px,左右边距同时为10px,就可以简写为margin:20px 10px;第一个20px代表垂直方向,第二个10px代表水平方向。如果垂直方向不相等,水平方向相等。就可以写成margin:5px 10px 2px;最后一个左边的值和右边相等可以省略不写。如果上下相等,左右不相等,就只能乖乖的写齐四个数。

    所有的浏览器都支持margin属性,需要一提的是任何版本的IE浏览器都不支持margin:inherit;为什么不能继承呢?我想和margin为负有关系,嗯一定是这样的。爹对妈能做的事情有些儿子还真不能做。哈哈哈~继承么,试想啊,给一个<div>设置了margin-top:10px;里面的子元素<p>也是margin-top:10px了么?另外提一下<p>的默认margin:16px 0;小tips:CSS中零值可以不写单位,直接写0就行了。

    折叠现象

    margin的折叠或者叫做margin的塌陷,有一个更专业的术语来称呼这种现象——collapsing margins。举个例子来说明一下。比如给<ul>下面的<li>设置margin:5px 10px;含义是垂直方向各是5px,水平方向各是10px;在没有做浮动的情况下多个<li>垂直排列。

    那么<li>之间的间距是多少呢?你可能会说,这还不简单,垂直方向上下都是5px,每一个<li>的下边距和它下方<li>的上边距相加不就是两者的间距啊,这个问题的答案是10px。嘿嘿,不怀好意的笑完,我再告诉你10px的答案是错的。浏览器实际渲染的间隔结果展现出来的是5px。为啥是5px不是10px?我回答你,这种现象好似女朋友告诉你,让你离她远点,我估计没有人真的会去离她远点,大多数人都是会上去抱抱哄一哄。看下面的GIF动图,会比较直观一些。

    元素垂直排列设置margin会出现折叠现象

    上述的只是折叠现象之一,再看下其他的。现在有两个<div class="one"><div class="two">,两者是嵌套关系。one嵌套two,如果在one设置margin-top:30px;在two设置margin-top:20px;会是什么结果?布拉布拉布拉~

    理想模型下嵌套元素垂直方向margin设置渲染结果

    如果你脑海中构建的渲染结果和上图一样,那么恭喜~又错了!其实真实的渲染结果是:

    嵌套元素垂直方向margin实际渲染结果

    WHAT!?什么情况?又来?一句“知道真相的我眼泪流下来”表达心中的感慨。我不知道第一次面对这种现象的时候你的状态是什么,反正刚开始接触前端的时候,我是被这个折叠问题折磨的不要不要的,甚至已经开始怀疑人生。

    刨根问底

    稍安勿躁,抱着探根问底的精神,认真查资料、查文档才能让自己的技术有质的飞越。为了不让“自己知其然不知其所以然”,找了一圈资料才发现其实早在CSS1.0的时代官方就给出过这种现象的说明。

    原文:The width of the margin on non-floating block-level elements specifies the minimum distance to the edges of surrounding boxes. Two or more adjoining vertical margins (i.e., with no border, padding or content between them) are collapsed to use the maximum of the margin values. In most cases, after collapsing the vertical margins the result is visually more pleasing and closer to what the designer expects.

    翻译:外边距用来指定非浮动元素与其周围盒子边缘的最小距离。两个或两个以上的相邻的垂直外边距会被折叠并使用它们之间最大的那个外边距值。多数情况下,折叠垂直外边距可以在视觉上显得更美观,也更贴近设计师的预期。

    为毛线这帮CSS的设计者们要搞出个这个飞机来折磨技术人员?想了半天终于释怀了,因为CSS的基本模型是排版,现在的前端工程师使用CSS主要是用来布局而不是文字排版。

    CSS并没有对布局和排版进行界定区别。而collapsing margins存在的初衷是解决文字排版段落间的间隔而存在的特性,所以collapsing margins才会给设计者们带来巨大的困惑。再回到上面看第一个关于<li>的例子那里,看图片中那种等边距设计在视觉效果看上去是不是显得很协调?这就是“更贴近设计师的预期”的含义。

    上段文字并不能很好的解释第二个嵌套的例子中的现象。第二种情况看上去并不是十分符合collapsing margins的定义。造成现象出现的原因其实是由另一个CSS特性决定的——即对于有块级子元素的父级元素高度的计算方式。如果父元素没有垂直边框(border)和填充(padding),那其高度就是其子元素顶部和底部边框边缘之间的距离,也就是子元素的height属性。所以子元素设置margin才不会在离开父级元素的上边。其实关于嵌套<div>例子中的的图片画的不是很准确,但是这样画才能更直观突出问题所在也更美观,跪求不喷。

    做学问要“存不疑”,要想搞清楚还是要仔细看下官方关于collapsing margins的解释

    In CSS, the adjoining margins of two or more boxes (which might or might not be siblings) can combine to form a single margin. Margins that combine this way are said to collapse, and the resulting combined margin is called a collapsed margin.

    翻译:在CSS中,两个或两个以上的块元素(可能是兄弟,也可能不是)之间的相邻外边距可以被合并成一个单独的外边距。通过此方式合并的外边距被称为折叠,且产生的已合并的外边距被称为折叠外边距。

    问题来了,什么叫相邻的?官方给出了一大堆文字,我英语水平差到没朋友,还是直接说明吧,处于同一个块级上下文中的块元素,没有行框、没有间隙、没有内边距和边框隔开它们,这样的元素垂直边缘毗邻,则称之为相邻。仔细观察的你肯定会发现我上面的两个例子中都是在垂直方向上产生的折叠,我想说是的,折叠现象只会在垂直方向上产生,在水平方向的margin是不会出现折叠的。

    垂直方向的margin会出现折叠,又是相邻元素。那么垂直相邻元素是啥?我蹩脚的英语又要上场了。

    • both belong to in-flow block-level boxes that participate in the same block formatting context
    • no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
    • both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
      1. top margin of a box and top margin of its first in-flow child.
      2. bottom margin of box and top margin of its next in-flow following sibling.
      3. bottom margin of a last in-flow child and bottom margin of its parent if the parent has ‘auto’ computed height.
      4. op and bottom margins of a box that does not establish a new block formatting context and that has zero computed ‘min-height’, zero or ‘auto’ computed ‘height’, and no in-flow children

    翻译:

    • 元素的上外边距和其属于常规流中的第一个子元素的上外边距。
    • 元素的下外边距和其属于常规流中的下一个兄弟元素的上外边距。
    • 属于常规流中的最后一个元素的下外边距和其父元素的下外边距,如果其父元素的高度计算值为 auto。
    • 元素的上、下外边距,如果该元素没有建立新的块级格式上下文,且 min-height 的计算值为零、height 的计算值为零或 auto、且没有属于常规流中的子元素。

    呵呵,英语实在太差了,讲究不了信达雅了。直接百度翻译搞定。将就着看吧,上述文字的最直观的收获是说得是发生margin折叠的元素不一定是兄弟关系,也能是父子或祖先的关系。这就能解释为什么上文中<div>嵌套也是折叠现象中的一种。后两段包含height:auto;的两句话,也表明了那个高度的计算方式的根源出处。

    这一part基本可以pass了,该是做总结的时候了。综合上述官方的说明可以得出几点很有用的信息。

    • 根元素不会发生折叠;
    • 块级元素才会有折叠;
    • 发生折叠需要是相邻的非浮动元素;
    • 折叠发生在垂直外边距上,即margin-top/margin-bottom
    • 含有边框、填充距不会有折叠。
    • 折叠后取其中最大的那个margin值作为最终值;

    关于最后一个折叠后margin的取值问题,还要多两句嘴。由于margin可以取负值,所以margin折叠的最终取值会三种情况发生。正正、正负、负负。就取值问题整了个列表方便查看记忆。

    • 参与折叠计算的都是正数的情况下,取最大值为折叠后的最终值。
    • 参与折叠计算的都是负数的情况下,取绝对值最大的值为折叠后的最终值。
    • 参与折叠计算的有正数也有负数的情况下,先取出负值中绝对值最大的值然后和正数中最大的数相加的结果就是折叠后的最终值。

    避免折叠

    折叠的来龙去脉已经被我们探究的差不多了,那么如何在日常工作中怎么才能避免被折叠现象给坑了?我们先来看看折叠发生的条件,然后反其道而行之就能避免遭遇折叠了。

    • margin 折叠元素只发生在块元素上;
    • 浮动元素不与其他元素 margin 折叠;
    • 定义了属性overflow且值不为visible的块元素,不与它的子元素发生margin 折叠;
    • 绝对定位元素的 margin 不与任何 margin 发生折叠。
    • 特殊:根元素的 margin 不与其它任何 margin 发生折叠;
    • 如果常规流中的一个块元素没有 border-top、padding-top,且其第一个浮动的块级子元素没有间隙,则该元素的上外边距会与其常规流中的第一个块级子元素的上外边距折叠。
    • 如果一个元素的 min-height 属性为0,且没有上或下边框以及上或下内边距,且 height 为0或者 auto,且不包含行框,且其属于常规流的所有孩子的外边距都折叠了,则折叠其外边距。

    好吧,条件还挺多。不过难不倒胆大心细脸皮厚的我们。按照一般常理是条件越多,避规的手段也就越多。如此看来能避规折叠的手段应该不会少了,其实在这么奇葩条件下我们都能遇见折叠,也算运气了。

    避规折叠,首先就是修改块级元素变为非块级元素,如果不清楚块级元素和行内元素的区别,出门左转《行内元素和块级元素的那些事儿》有详细说明。还能让元素浮动来避规折叠。还能设置border、绝对定位、设置overflow:hidden;看具体情况来用不同的方法。个人用的最多的就是overflow:hidden;因为设置border多出来的像素会让布局上有些小差异,设置绝对定位会让元素脱离文本流。

    好了,洋洋洒洒的写了这么多,margin的折叠你也许已经弄的很明白了,以后的工作或学习中遇到这个问题应该能够得心应手、收放自如、信手拈来了。

    关键点补充

    CSS2及后续的规范中,将margin折叠描述得更为详尽了。例如在水平书写模式下,发生margin折叠的是垂直方向,即margin-top/margin-bottom,在垂直书写模式下,margin折叠发生在水平方向上,即margin-right/margin-left

    你问书写方式怎么改?writing-mode啊,它有两个主要参数lr-tb: 左-右,上-下。即水平逐行书写,这个也是现代人的书写习惯;tb-rl: 上-下,右-左。即从上到下书写,写满一列,向左侧折列。和中国古代书写方式一样。如果修改了书写方向变成垂直了,那么collapsing margins就会在水平方向发生,而不会在垂直方向上出现。

    先别高兴的说,我刚才写出来writing-mode的值什么lr-tbtb-rl啊,其实是老IE浏览器的语法。有多老?IE7版本都能支持这两个值,够古老了吧。先提老语法是想强调writing-mode属性不是多么高大上新的东西,只不过我们用的不多所以才不熟悉。现在我们怎么书写属性的值呢?很简单:

    writing-mode: horizontal-tb;    /* 默认值 */
    writing-mode: vertical-rl;     /*垂直书写,从右向左阅读(和汉字古文一样)*/
    writing-mode: vertical-lr;     /*垂直书写,从右向左阅读(只是方向发生变化)*/
    

    上个图,说明问题~~


    由于writing-mode修改了书写顺序collapsing margins也从垂直变成了水平

    最后我写了一个综合性的demo,来全面的展示下margin折叠的尿性。包括修改书写方向的示例。想看demo的就>>>猛戳这里<<<

    全文完~后面没有了

    相关文章

      网友评论

        本文标题:浅析让人D疼的margin折叠

        本文链接:https://www.haomeiwen.com/subject/cbrxlftx.html