再谈CSS中MARGIN属性对水平格式化的影响

盒子模型,用户代理如何格式化盒子模型在水平方向上的空间问题?再谈CSS中MARGIN属性对水平格式化的影响。

在家整理客厅的抽屉,无意间翻到一堆旧照片,真佩服以前的照相师傅,随意的按下快门就能定格住 片刻永恒——没有太刻意的摆拍,家具琐碎物品刷存在感的穿帮出镜都让如今再看变得弥足珍贵,现在倒希望有更多的穿帮镜头。这张照片大约是我刚出生几个月时拍摄的,问老爸老妈,也不记得具体的时间了。照片中的是奶奶的四个儿子(不好形容,很。。非主流?很有年代感!),最左边的那位就是我老爸。岁月真是神奇的东西,谁会知道未来会与什么不期而遇,老爸遇见我,我遇见了你,我遇见了未来,还是未来遇见了我… …

盒子模型

典型的盒子模型包含以下属性,从外边界到内容区顺序如下:

  • margin (margin-top, margin-right, margin-bottom, margin-left)
  • border (border-top, border-right, border-bottom, border-left)
  • padding (padding-top, padding-right, padding-bottom, padding-left)
  • content (width, height)

本篇要谈论的是用户代理如何在水平方向上将空间格式化,因此,下面会用到的只有与水平方向相关的属性:

  • margin-left(左外边距)
  • border-left(左边框)
  • padding-left(左内边距)
  • width(宽度)
  • padding-right(右内边距)
  • border-right(右边框)
  • margin-right(右外边距)

默认情况下,块级元素的width的属性值就是content部分的宽度,特殊情况是,设置box-sizing为border-box,将使用IE旧版浏览器下的一个css渲染计算方式——width属性值除了包含content部分的长度外,还包括padding和border的宽度。box-sizing属性的默认值为content-box,顾名思义——width属性值级是content部分的宽度。(由于本篇我打算谈论水平方向上的格式化,因此只包含水平方向的宽度属性,height同理。)

box-sizing: content-box;  //默认值
box-sizing: border-box; //从IE旧版浏览器的渲染方式借鉴过来放入标准中

下文一些词语容易混淆,在这里特别说明一下,元素的宽度——指的是整个盒子模型的宽度而不仅是指content的宽度。

横向尺度

用户代理在格式化块级元素盒子模型时,有一个原则是:总会让元素的宽度占满父容器的整个content宽度。

而这一原则正好解释了为什么块级元素会独占一行,那么用户代理是如何能让元素独占一行的,这就不的不说说margin-left, width 和 margin-right 之间的故事了。

先从一个最常见的最佳实践入手,你肯定知道如何将一个块级元素水平居中的方法的其中一个,最常见的就是将元素的margin-left和margin-right设置为auto。

这个auto属性就厉害了,正是这个属性值告诉用户代理,这一行上剩余的空间需要用这个被设置为auto的元素填满,用户代理就需要暗地里计算出auto的大小,你说auto神奇不神奇,计算出的结果使得上方水平方向上的7个属性和必须为正好是父元素的content宽度。

我们不妨先把用户代理的一套计算逻辑列出来:

指定宽度

当元素的width属性指定了长度单位,margin-left和margin-right均设置为auto时,用户代理为了让这7个属性相加正好为父元素的content宽度,会计算出这一行剩余空间的长度,将剩余空间一分为二,分别给了margin-left和margin-right,所以margin-left和margin-right相等,表现为元素在父元素中水平居中。

我是水平居中的块级元素哦
我的margin-left和margin-right都是auto,width是200px

其中一个为auto

当width,margin-left和margin-right其中一个值为auto,而余下的两个属性值指定为特定的值,那么用户代理会根据计算出那个指定为auto的属性的值,以满足该元素能占满一行。

其中一个属性值为auto的情况有三种,我分别只将margin-right,width和margin-left设置为auto来检验表现:

只有我的margin-right设置了auto值,margin-left为20px,width属性为120px。

只有我的width属性设置了auto值,margin-left和margin-right为50px。

只有我的margin-left属性设置了auto值,margin-right为20px,width为130px。

均设置指定值

如果这三个属性都设置为了特定的值,没有一个为auto,那么这一行剩下的空间用户代理又将如何格式化呢?答案就是,根据用户的阅读习惯,用户代理会强制性的将margin-right或margin-left设置为auto,并计算其所占宽度。对于习惯从左向右阅读的用户,比如汉语,英语等等大多数语言,则用户代理不管开发者是如何设置margin-right属性值的,统统强制将margin-right设置为auto。

width属性为130px,margin-left和margin-right都设置为20px,可是用户代理表示然并卵。。把我的margin-right属性值隐式设置为了一个计算值

所以,这种情况下,但凡知道这件趣事的开发者都不应该再设置margin-right的属性值了,因为然并卵。

负外边距

在水平方向上,和另外5个属性不同的是,margin-left和margin-right是可以设置负值的,而另外5个属性必须不能设置负值。

为什么margin可以设置为负值,这时用户代理给自己留的一条后路。

根据那一条最简单的规则:水平方向上,7个属性值的和为父元素的content的宽度,因此这七个属性满足 宽度总和守恒,如果margin-left或margin-right为负值,则剩余的属性的长度值势必要增加来维持宽度综合守恒的规则不被打破。

一般情况下,只要水平方向上这7个属性都是大于或等于0的,元素就不会大于其父容器的内容去宽度。不过考虑以下情况:

div { width:400px; border:3px solid black; }
p.write { margin-left:10px; width:auto; margin-right:10px; }

A paragraph

Another Paragraph

第一个p标签应用了write类,细心的你会发现第一个p标签右侧超出了父容器,我们不妨先根据上面4个规则分析一下:

margin-left和margin-right均为固定值,width为auto,符合规则2:其中一个为auto,所以width会实时被设置为剩余空间的长度,也就是width是会根据具体情况变化的。这里的margin-right设置为负值,势必会让7个水平方向上的属性值总和减少,因此随机应变的width属性便派上用场,width属性被设置为margin-right值的绝对值这么长,保证了最后相加为父元素内容区的宽度。

尽管这导致子元素超出了七父元素,但并没有违反规范,因为7个属性值加在一起仍等于所需的总宽度。

当然如果为元素设置了边框大小和内边距,width随机应变会进一步减小。

相反,如果我们设置元素的width属性超出了父元素的内容区,那么不管我们是否设置了margin-right的值,用户代理都会强制的将margin-right设置为auto,并为margin-right计算一个负值与超出的长度相抵消。

除了设置margin-right为负值,margin-left也可以设置为负值,道理都相同,不过是向父元素的左侧溢出。

如果width、内边距和外边距设置为百分数,回应用同样的基本规则,值声明为百分数还是长度并不重要。

本篇完 ·END·

下一篇《浅学先秦《周易》之《乾》卦》

上一篇《从0,1,0,1规则到CSS选择器特殊性优先级》

永久链接 http://www.shuaihua.cc/article/margin-property-affect-horizontal-level-layout

快速跳转 心头好文 - css - 《再谈CSS中MARGIN属性对水平格式化的影响》

发布日期 2017年12月8日 星期五

版权声明 自由转载-非商用-非衍生-保持署名(创意共享3.0许可证