您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:如何营造和睦的文化系列,前端布

新葡亰496net:如何营造和睦的文化系列,前端布

发布时间:2019-11-05 00:41编辑:新葡亰官网浏览(85)

    前端布局基础概述

    新葡亰496net:如何营造和睦的文化系列,前端布局底子概述。2018/04/02 · CSS · 1 评论 · 布局

    原文出处: 蔡剑涛   

    1. 前言

    前端圈有个“梗”:在面试时,问个css的position属性能刷掉一半人,其中不乏工作四五年的同学。在公司一直有参与前端的基础面试,深感这个“梗”不是个玩笑。

    然而,我觉得实际比例可能会更高,甚至很多面试官自己也未必真正掌握。因为大部分前端同学,可能不知道初始包含块的概念,或知道但对这个概念理解有误。

    造成这种现象的原因主要有两方面,一方面是在介绍这个知识点时,网上有谬误的文章太多,国内外亦如此(MDN也名列其中),导致很多同学被误导(我一开始也是),而且这种错误被代代相传;另一方面可能是我们平时不太注重概念的定义、自身对待知识的态度还不够严谨、缺乏验证精神和系统总结的习惯。

    一次偶然的机会,我发现了这种谬误,并找到了W3C组织对初始化包含块的官方定义,也为了让刚入前端圈的同学少走一些弯路,在此我想借本文分享给大家(详述请见5.5. 包含块章节),也系统分享一下,本人在前端布局基础方面积累的浅薄经验。(因为是系统概述,所以篇幅会比较长,希望各位读者有心理准备)

    2. 什么是前端布局基础?

    前端布局方案主要有三种:

    • 传统布局方案(借助浮动、定位等手段)
    • flex布局方案
    • grid布局方案

    这些方案都能够解决布局问题,而且每个方案都有各自的理论基础,那么哪一个方案的基础理论可以称得上是前端布局基础?要回答这个问题,我们还得深入去了解这三种方案的特性。

    传统布局方案,需要使用者熟练掌握元素的分类及布局特性、浮动原理和定位原理等众多基础知识,方能在解决各类前端布局问题时游刃有余,这不仅学习成本大,而且实现的复杂度也高,实现的CSS代码也不够精简、优雅。但由于其基础知识来源于CSS2,所以浏览器兼容性最好,对于用户是友好的。

    flex布局方案,正是为了解决传统布局方案的种种不便,而提出的一种新型改进方案,它不再需要借助浮动和定位等布局手段,而是通过父元素(flex box)单方面配置相关的CSS属性来决定子元素的布局规则,且在大多情况下无需子元素(flex item)参与,就能完成子元素间的布局问题,不仅学习成本低(公司之前有几个后端工程师亦能快速上手),且大大简化了布局的实现复杂度,CSS代码也更加精炼。美中不足的是IE10才开始支持,且需要使用-ms-前缀(IE11无需)。

    虽然现今的手机多使用的是现代浏览器,对flex支持度较好,然而并不是每一款手机都如此:笔者曾在一个移动端项目采用过flex布局方案,然而公司的测试同学在“华为荣耀5”的自带浏览器,检测到无法支持flex布局,我们能够跟测试的同学说,是这款华为手机的浏览器有问题吗?显然不能。于是故笔者在项目早期就及时放弃了flex布局方案,改用传统布局方案实现,避免了后面大规模的改动。

    grid布局方案,是由微软提出,相对于传统布局方案和flex布局方案,它是一种二维布局方案,在IE10开始支持,但需要使用-ms-后缀(IE11 不再需要)。

    总的来说,这三类方案都能基本解决日常的前端布局问题,且从易用性、灵活性和强大性来说,flex布局和grid布局更是未来的趋势。但是从当前各版本浏览器在用户市场上的使用情况和各方案的浏览器兼容性来看,传统布局方案对用户最友好,具有一定的不可替代性,所以我觉得,传统布局方案是最应该先掌握好的,尤其是对于在to B企业工作的前端同学来说。

    所以本文将详细介绍的“前端布局基础”,指的是围绕着“传统布局方案”的众多CSS知识,其主要内容来源于CSS2规范。

    3. 为什么要学好前端布局基础?

    页面写多了的前端同学,我想应该都会有这样一个深刻的感受:在编写页面时,经常会遇到不同场景的布局问题,我们不仅需要针对特定的场景选定可实现的布局实现方案,而且需要考虑未来可能发生的变化。

    而要做好这一点,就需要扎实的前端基础作为依托。

    所以在我看来,学好前端布局基础,其目的是为了在面对不同场景的布局问题时,能够提出一种合理的布局方案:既能解决问题,又能最大程度地拥抱变化。

    4. 量化布局方案的合理性

    前面提到过的“解决问题”、“拥抱变化”,仅仅是合理布局方案的两大核心目标,如果想要让目标更好地落地,我们仍需要一些量化合理性的原则,来提升对目标的方向感,以让目标变得更加可执行。

    说到量化“解决问题”这个目标,对于即写即呈现的前端代码来说,我们可以很直观地判断一种方案是否可行,所以不需要太多的量化手段,我们主要是要量化“拥抱变化”这个目标。

    要想量化“拥抱变化”这个目标,我们首先得清楚“变化”有哪些。笔者根据过往的开发经验,将变化分为两大类:一是布局需求的变化,二是运行环境的变化。

    而针这这两类变化,我提出如下量化原则:

    一、对于布局需求的变化,可以做到:

    • 方便快速定位需修改的位置
    • 能够不花或用最少的修改成本应对变化

    二、对于运行环境的变化,可以做到:

    • 在不同浏览器均有正确或良好的显示

    如果一个方案能够体现以上几点原则,我认为可以称得上是一个合理的方案。最后,我将布局实现方案的合理性归纳为:方案在满足正确性的前提下,其实现逻辑规范、实现职责分明且拥有良好的浏览器兼容性。

     

    下面我们正式开始介绍与“传统布局方案”相关的布局基础知识。

     

    5. 布局基础要点

    5.1. CSS标准盒模型(或W3C盒模型)

    一个web页面是由众多html元素拼凑而成的,而每一个html元素,都被解析为一个矩形盒,而CSS盒模型就是这种矩形盒的解构模型。CSS盒模型,它由内到外、被四条边界Content edge、Padding edge、Border edge和Margin edge划分为四个区域:Content area、Padding area、Border area和Margin area,在形状上,Content area(又称content-box)是实心矩形,其余是空心环形(空心部分是Content area),如下图所示:

     

    新葡亰496net 1

    CSS盒模型-区域划分图

    此外,每个区域都有其特定的作用:Content area,是当前元素用来容纳所有子孙元素;Padding area,是当前元素用来隔离自身和子孙元素;Border area是当前元素用来显示自身的轮廓;Margin area,是当前元素用来隔离自身和相邻元素。理解每个区域的作用和职责至关重要,有助于我们写出优雅、清晰的布局代码。

    新葡亰496net 2

    CSS盒模型-区域作用图

     

    而每个区域的尺寸,又分别由特定的CSS属性来控制,如下图所示:

     

    新葡亰496net 3

    CSS盒模型-属性控制图

    这些CSS尺寸属性(width、height、padding、border和margin),相当于一个个hook,我们可以通过设置这些“hook”来达到调整元素尺寸的目的。

    5.2. box-sizing(CSS3属性)

    5.2.1. box-sizing的作用

    box-sizing,顾名思义,其作用与设置CSS box的尺寸大小有关,而CSS box又可细分为:

    • content-box(即content area)
    • padding-box(=content area padding area)
    • border-box(=content area padding area border area)
    • margin-box(=content area padding area border area margin area)

     

    简单来说,box-sizing的作用就是告诉浏览器:CSS属性width和height是用于设置哪一种box的尺寸,在W3C标准中,box-sizing的值仅有content-box和border-box(firefox则额外支持padding-box)。所以,

    当box-sizing的值为content-box(默认值)时,有:

    width = content-width;

    height = content-height;

    当box-sizing的值为border-box时,有:

    width = content-width padding-left padding-right border-left-width

    • border-right-width;

     

    height = content-height padding-top padding-bottom border-top-height border-bottom-height;

    关于box-sizing的作用,还有另一种表述:告诉浏览器,是使用W3C盒模型,还是使用IE盒模型。

    5.2.2. box-sizing的浏览器兼容性

    box-sizing是CSS3属性,在IE8 (包含IE8)开始支持,然而在IE8,box-sizing的值为border-box时,不能与min-width, max-width, min-height或max-height的一起使用,因为IE8对min-*和max-*的解析,仍是作用于content-box,不受box-sizing属性控制。

     

    5.2.3. box-sizing的产生原因

    仅仅掌握box-sizing的基础使用,是无法真正理解box-sizing的作用,所以要想把box-sizing用好,我们还得从CSS盒模型的发展史来深入理解box-sizing的产生原因。

    在CSS的发展历程中,有两个版本,一个是IE盒模型,另外一个是W3C盒模型。IE盒模型,在IE5-(包含IE5)和navigator4上均有使用;而W3C盒模型,在IE6 (包含IE6)标准模式开始得到支持。两种版本的盒模型,其实在模型结构上是一致的,只是with和height属性的计算规则不一样,其区别,等价于“box-sizing的两个属性值border-box和content-box的区别“,如下图所示:

    新葡亰496net 4

    IE盒模型和W3C盒模型的区别

    在了解了CSS盒模型的发展历程,以及后来新增的box-sizing的开始支持时间,我们不难发现:

    • IE5-采用IE盒模型
    • IE6、7的标准模式放弃了IE盒模型,转为使用W3C盒模型
    • IE8 借助box-sizing,又重新提供了对IE盒模型的支持

    对于IE盒模型,我们看到了W3C组织先去后留的反复态度,我不禁提出以下两点疑惑:

    问题一: 为什么W3C组织在制定盒模型标准时,一开始会放弃IE盒模型,而重新建立以content-box为计算规则的W3C盒模型?W3C盒模型比IE盒模型好在哪里?

    问题二:为什么在CSS3中,又重新提供了对IE盒模型的支持(box-sizing设置为border-box),又是基于哪方面的考虑?

    关于第一个问题,本人并没有找到相关的官方说明,但我比较认可的一种说法是:

    在日常生活中,我们在放东西时,会关心东西放到多大的盒子里面,这里的“多大”,往往指的是盒子的容量,而不是整个盒子的尺寸。而HTML元素也被看成是一个盒子、一个容器,相应地,我们也会更关注其内容区域的尺寸,也更希望对内容区域有更强的控制力。所以,从存储的角度来看,W3C盒模型更符合这种认知,借助width和height,我们可以通过声明的方式,直接设置conent-box的尺寸。而如果采用IE盒模型,我们只能先设置整个盒子的尺寸(border-box),最后由浏览器自动计算出content-box的尺寸,显得对content-box尺寸的控制力较弱。

    关于第二个问题,我认为有以下几个原因:

    1. 有助于复用基于IE盒模型开发的CSS代码;

    2. IE盒模型的“遗老遗少”可以延续计算习惯;

    3. 部分html元素,在解析时依然采用IE盒模型的计算规则(这样的元素有select、button),使用IE盒模型有助于保持一致性;

    4. 从元素布局的角度来看,IE盒模型的width和height的语义更符合人类的直观认知(盒子的尺寸、轮廓应该以border为界);

    5. 在弹性布局和响应式布局场景,IE盒模型比W3C盒模型表现更佳(更容易实现、浏览器兼容性更好),如设置某个元素的宽度始终占当前行总宽度的固定百分比(小于100%),并且该元素拥有固定像素的padding;

    举个例子:设置一个元素,其宽度分别为当前行的40%,且该元素的padding固定为10px。

    IE盒模型的实现方案:

    方案一: 使用一个div即可实现,直接设置width为40%,padding为10px;

    W3C盒模型的实现方案:

    方案一:使用两个div模拟实现,外层div的width设置为40%,内层div的padding为10px,                   width为auto;

    方案二:使用一个div即可实现,但是需要借用CSS3的calc函数,动态计算其内容区域的宽度,即width为calc(40% – 20px), padding为10px;

    显然,IE盒模型的实现方案更加简洁,而且浏览器兼容性更好。

    对上述两个问题的解答,其实也是对IE盒模型和W3C盒模型的一个比较。我们可以从比较中,明晰两种盒模型各自的优缺点。同时,经过大量的实践经验证明和充分讨论,IE盒模型总体上是优于W3C盒模型,这也是IE盒模型能够“王者归来”,被W3C组织重新启用的真正原因。

    于是乎,为了重新在新规范中支持IE盒模型,也为了向后兼容W3C盒模型,W3C组织在CSS3中添加了box-sizing属性,用于切换这两种盒模型。

    5.2.4. 对box-sizing的评价

    在我看来,在CSS3中添加box-sizing其实是一种比较trick的弥补方式。虽然这种设计能重新提供对IE盒模型的支持,但是在某种程度上,造成了CSS属性width和height具有二义性,使其职责变得不单一。然而这似乎又是最可取的修正方案了,因为在网上已经存在了大量基于W3C盒模型开发的网页,后续的修正方案不得不考虑向后兼容。我们只能在不合理设计的基础上,再次用不优雅的设计来解决新的问题。

     

    如果能够穿越时空,回到W3C组织在讨论“如何设计标准盒模型”时,我认为更合适的设计方案是添加新的属性单独用于设置content-box的尺寸,而保留IE盒模型width和height原来的语义。这样就不会有后来的box-sizing属性。

    我猜想W3C组织也想过这种方案,但是当时可能认为:

    1. 直接设置元素border-box尺寸的意义不大,且border-box的尺寸设置也能够通过设置content-box的尺寸来实现;(其实同时两种支持content-box和border-box尺寸的设置也无妨,完全可以当做是语法糖)

    2. 设置content-box尺寸又属于高频操作,若新增的属性命名为content-width或content-height则显得名称太长;(命名为cwidth和cheight也行)

    基于这两点,最终提出了用width和height来设置content-box尺寸的解决方案,也就是如今我们看到的W3C盒模型。

    纵观CSS盒模型的发展史和box-sizing的创建原因,感触比较深的就是:不合理的设计并不是总会被修正,因为既有实现的广泛应用,会使得其被继续遵循。而后续的新增设计,也是建立在先前不合理设计的基础上。这是否也验证了黑格尔的哲学名言:存在即合理?

    关于对box-sizing的评价和思考,可能显得有一些马后炮,一些猜想也可能只是笔者的凭空臆想,并非W3C组织原意。在这里只是为了分享我对重构的一些思考,也是为了与和我有同样疑惑的同学做个交流。

    5.2.5. box-sizing的最佳实践

    在这里主要回答三个问题:

    问题一:box-sizing的值,取content-box好,还是取border-box值好?

    如果最低需要兼容IE6、7,那么box-sizing不可使用,只能使用W3C盒模型;

    如果最低只需兼容IE8,那么使用content-box在功能上完全没有问题,只是在一些弹性布局和响应式布局实现上,会稍微麻烦一点;而border-box虽然在这些方面表现更好,但是不能和IE8的min-width、min-height、max-width和max-height四个属性一同使用,使用的话就要稍微注意一下;

    如果最低只需兼容IE9,那么本人觉得,全局配置取content-box更为合适,局部配置二者均可。原因如下:

    1. CSS3提供了calc函数(IE9 ),使得W3C盒模型有了强有力的助攻,在弹性布局和响应式布局的表现,与IE盒模型无异;

    2. 默认优于配置原则:我个人认为,“默认优于配置”,特别是在reset.css这种架构级、平台级的配置文件,要尽量避免对未来可能引入的模块有侵入性。譬如,我们在一个项目中时常需要引入第三方组件,如果这个组件没有强声明box-sizing,那么其默认使用的就是W3C标准盒模型,如果在全局的reset.css中设置box-sizing的值为border-box以选用IE盒模型,那么就会影响到这一类默认基于W3C盒模型的第三方组件的样式。这里也给我们提了一个醒,在封装组件时,记得强声明box-sizing,哪怕你使用默认的content-box。

    总之,大部分场景二者可以互换,只是使用理念不一样。小部分场景border-box更具优势,但随着calc函数的支持,这种优势已经不再,相反content-box是默认值的优势愈加明显。

     

    我个人建议是:全局使用默认W3C盒模型(你的CSS代码最低能够兼容IE6/7,在IE8也可以和min-*和max-*一起使用),局部场景二者均可(仅把IE盒模型当作是一种布局技巧来使用)。你喜欢全局使用IE盒模型也是可以的,只要确认项目只需要兼容到IE8,即便有可能影响到引入的第三方组件,也是有办法处理的。

    问题二:如果想要全局使用IE盒模型,那么在reset.css中,该怎样设置box-sizing?

    这里提供一个参考:

    html { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *, *:before, *:after { -webkit-box-sizing: inherit; -moz-box-sizing: inherit; box-sizing: inherit; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    html {
     
    -webkit-box-sizing: border-box;
     
    -moz-box-sizing: border-box;
     
    box-sizing: border-box;
     
    }
     
    *, *:before, *:after {
     
    -webkit-box-sizing: inherit;
     
    -moz-box-sizing: inherit;
     
    box-sizing: inherit;
     
    }

    这样设置的好处有:

    1. 子元素的盒模型类型,默认由父元素决定,方便组件统一设置;

    2. 支持低版本的浏览器:Safari (< 5.1), Chrome (< 10), and Firefox (< 29);

    问题三:Bootstrap3开始,全局使用IE盒模型(box-sizing取border-box),又是基于怎样的考虑?怎么协调好与基于标准盒模型开发的第三方组件的关系?

    众所周知,BS2还考虑对IE7的兼容,而BS3彻底放弃了对IE7的兼容,并将box-sizing设置为border-box。关于这一点,可见“Bootstrap 3 released”官方发布的change list,摘录如下:

     

    新葡亰496net 5

    从以上直白的表述中:Better box model by default(默认使用更好的盒模型),我们可以看出BS作者是IE盒模型的拥趸。作者也把理由罗列了出来,其核心内容也是如前面所提到的,IE盒模型在响应式布局上的良好表现。补充的一点是,如果不全局设置border-box,而每个组件及其子组件单独设置,维护起来将是个梦魇(作者在官方编号为12351的issure中有提到)。

    而关于BS如何处理好与基于标准盒模型开发的第三方组件的关系,亦可参见编号为12351的issue:”Move away from * {box-sizing: border-box } to play nice with 3rd party scripts”

    作者在issue中,霸气又委婉地回应:

    1. BS并不考虑对第三方组件和框架的支持。作者委婉地说,BS是一个大项目,活跃维护者也主要是四个人,顾不来所有人的需求啊~(但感觉作者是在说,BS是个又大又全的框架,你丫还搞第三方组件干嘛呀)

    2.IE盒模型,用了大家都说好,为什么第三方组件不转过来支持IE盒模型啊(果然是铁粉)

    本章节从box-sizing的作用、浏览器兼容性、产生原因、评价和最佳实践这5个切入点,来讲解box-sizing属性,以期加深各位同学对这个属性的理解和掌握。特别要强调的一点是,如果刚接手某个项目,在编写CSS代码前,先看看项目是否有全局配置box-sizing,并根据具体的取值来选用相应的尺寸计算规则。

    5.3. 元素的分类及其布局特性

    5.3.1. 元素的分类

    从元素的布局特性来分,主要可以分为三类元素:block-level(块级)元素、inline-level(行内级)元素和inline-block-level(行内块级)元素,我们可以对其下个定义:

    5.3.1.1. 块级元素

    display属性取block、table、flex、grid和list-item等值的独占一行显示的元素。

    5.3.1.2. 行内级元素

    display属性取inline值的可在同一行内排列显示的元素。

    5.3.1.3. 行内块级元素

    display属性取inline-block、inline-table、inline-flex和inline-grid等值的兼具块级元素和行内级元素布局特性的元素。

    友情提示:

    1)关于各类元素display的取值,实际已全部罗列,但为了保证定义能拥抱变化(未来可能引入新的display属性值),在罗列时使用了等字;

    2)w3c官方文档,把display属性值为inline、inline-block、inline-table的元素,统称为inline-level元素,我不太喜欢也不太认可这种泛泛的分类,本文重新定义了一个“inline-block-level元素”的概念,来对“inline-level元素”进行了细分,并将inline-blocks、inline-tables单独分类为inline-block-level元素,原文档如下:“The following values of the ‘display’ property make an element inline-level: ‘inline’, ‘inline-table’, and ‘inline-block’.”

    5.3.2. 元素的布局特性

    5.3.2.1. 块级元素(block-level)的布局特性

    对于块级元素,有如下几个布局特性:

    1. 独占一行(width默认为100%,height为0);
    2. 可以设置任何尺寸相关的属性(width、padding、margin和border);

     

    5.3.2.2. 行内级元素(inline-level)的布局特性

    在讲行内级元素的布局特性之前,我们先了解一下行内级元素的分类,其可再细分两类元素:

    1)可置换行内元素

    在MDN中,其对“可置换行内元素”的定义如下:

    新葡亰496net 6

    按字面翻译,“可置换行内元素”,是展示内容不在CSS作用域内的元素。这句话是不是不好理解?我们可以换另外一种方式理解:“可置换行内元素”,是这样一类元素,其展示的内容是通过元素的src、value等属性或CSS content属性从外部引用得到的,可被替换的。随着内容来源或内容数量的变化,可置换元素本身也会有水平和垂直方向上尺寸的变化。典型的可替换元素有 <img>、<object> 、<video>  和 <embed>,表单类的可替换元素有 <textarea></textarea>和<input> ,某些元素只在一些特殊情况下表现为可替换元素,例如<audio> 、<object>、<canvas>和<applet>。

    特别地,通过 CSS content 属性来插入的对象又被称作 匿名可置换元素。

     

    2)不可置换行内元素

    “不可置换行内元素”其实就是我们常见的一类行内元素,这一类行内元素有<a>和<span>等。“不可置换行内元素”是相对于“可置换行内元素”的,其展示的内容是在CSS作用域范围内的,是不可替换的。

     

    言归正传,行内级元素有如下几个布局特性:

     

    1. 在一行内可以与多个同类型的元素按从左到右的顺序排列;
    2. 不可置换行内元素不能设置width、height和垂直方向上的margin,而可置换行内元素则可以;
    3. 在水平和垂直方向上的对齐方式,行内级元素分别受父元素的text-align属性和自身vertical-align属性的控制(父元素是table-cell元素时,也受父元素的vertical-align属性控制),在水平方向上默认左对齐,在垂直方向上默认在行框的baseline基线上显示(“行框”的概念,会在后面深入讲解);

     

    友情提示:

    1)有时候我们不必太纠结于哪些行内元素是可置换行内元素,因为有些浏览器(如chrome)的默认样式(user agent stylesheet),会将这一类元素重置为inline-block元素,我们可以统一把可置换行内元素理解为inline-block元素,因为其布局特性与inline-block-level元素相同。

    2)当inline-level元素水平排列时,两两之间可能会出现大约6px的空白,这是由元素间的空白字符(换行符、空格或制表符)产生,如下图所示:

     

    新葡亰496net 7

    清除方法有很多,本人习惯用浮动的方式来处理,其它方法可自行google。

    5.3.2.3. 行内块级元素(inline-block-level)的布局特性

    行内块级元素兼具block-level元素和inline-level元素的布局特性,主要体现为:

    1. 排列方式与行内级元素同,不独占一行,在一行内按从左到右的顺序排列;
    2. 水平和垂直方向上的对齐方式与行内级元素同;
    3. 和块级元素一样,可以设置任何尺寸属性(但width默认为0);

    注:我们不难发现,其实可置换行内元素,其布局特性与inline-block-level元素相同。

    5.4. 格式化上下文(Formatting Context)

    格式化上下文,它指的是具有某种CSS格式化规则(布局规则)的上下文环境,在这个上下文环境内的所有子元素,都将根据其特定的CSS格式化规则来进行排列。

    我们可以给某个作为容器的元素指定特定的格式化上下文,也就是说我们可以定义一个具有特定布局规则的渲染区域。常见的格式化上下文有BFC(CSS2.1 规范)、IFC(CSS2.1 规范)、 FFC(CSS3规范新增)和GFC(CSS3规范新增),具体介绍如下:

    5.4.1. BFC

    5.4.1.1. 定义

    BFC, 全称是block formatting context,它是一个独立封闭的渲染区域,在这个区域内的所有元素,从区域的顶部起,一个接一个地根据自身的布局特性进行排列:在这个区域内的块级元素 ,按从上到下的顺序显示,相邻的块级元素可以使用margin隔离,但在垂直方向上相邻的块级元素会发生margin合并;在这个区域内的inline-level或inline-level-block元素,则按从左到右的顺序显示(W3C组织说BFC内部的元素都是一个接一个地垂直显示,我觉得不是很严格,因为BFC内部也可以容纳inline-level和inline-level-block元素,所以这里我的解释和W3C还是稍微有一些不一样)。具有BFC格式化环境的元素,我们称之为BFC元素,可以说,BFC定义了BFC元素content区域的渲染规则。

    看到这段描述,是不是觉得BFC的渲染规则,不就是文档流的默认布局规则吗?确实很像,但不完全等同。BFC元素内部的渲染规则和普通块级元素内部的渲染规则,还是有一些不同的,我们将在5.4.1.3. 特性一节详述。

    5.4.1.2. 创建方式

    创建BFC元素的方式有如下几种(摘自MDN BFC):

    • 根元素或其它包含它的元素
    • 浮动元素 (元素的 float 不是 none)
    • 绝对定位元素 (元素的 position 为 absolute 或 fixed)
    • 内联块 (元素具有 display``: inline-block)
    • 表格单元格 (元素具有 display``: table-cell,HTML表格单元格默认属性)
    • 表格标题 (元素具有 display``: table-caption, HTML表格标题默认属性)
    • overflow 值不为 visible 的块元素
    • display``: flow-root
    • contain为以下值的元素: layoutcontent, 或 strict
    • 弹性项 (display``: flex 或 inline-flex元素的子元素)
    • 网格项 (display``: grid 或 inline-grid新葡亰496net:如何营造和睦的文化系列,前端布局底子概述。 元素的子元素)
    • 多列容器 (元素的 column-count 或 column-width 不为 auto, 包括 column-count: 1的元素)
    • column-span``: all应当总是会创建一个新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一个多列容器中。

    5.4.1.3. 特性

    BFC元素具有如下特性:

    1. 对应一个独立、封闭的渲染区域,子元素的CSS样式不会影响BFC元素外部;

    举个例子,我们分别用连续的两个块级元素,一个是普通块级元素,另一个是BFC元素(均使用绿色背景),分别包裹一个margin-top为20px的子元素(黄色背景),对比其布局效果:

    新葡亰496net 8

    说明:

    • 普通块级元素,其子元素的margin-top,不会隔开自身与父元素(普通块级元素),但是会作用到父元素外部(将父元素和叔伯元素或祖父元素隔开);
    • BFC元素,作为一个独立、封闭的渲染区域,其子元素的margin-top,则会隔开自身与父元素(BFC元素),而不会影响到父元素外部;

    2. 浮动子元素参与BFC父元素的高度计算,也就是BFC元素能够识别浮动元素(将元素声明为BFC元素,也是clearfix解决父元素塌陷问题的一种常用方法);

    举个例子:

     

    新葡亰496net 9

    说明:

    • BFC元素,能够识别浮动子元素,浮动子元素参与BFC元素的高度计算,不会出现“高度塌陷”问题;
    • 普通块级元素,不能够识别浮动子元素,会出现“高度塌陷”问题;

     

    3. 占据文档流的BFC元素(可使用overflow: auto创建),能够识别浮动的兄弟元素;

    举个例子:

    新葡亰496net 10

    说明:

    • 普通块级元素,不能够识别浮动的兄弟元素,会被浮动的兄弟元素覆盖部分内容;
    • 占据文档流的BFC元素(可使用overflow: auto创建),能够识别浮动的兄弟元素,不会被浮动的兄弟元素覆盖,与之同行显示;

    4. 占据文档流的BFC元素(可使用overflow: auto创建),width为auto时,会占满当前行的剩余宽度;

    举个例子:

    新葡亰496net 11

    说明:

    • 文档流中的BFC元素, width为auto时,会占满当前行的剩余宽度;

    5.4.2. IFC

    5.4.2.1. 定义

    IFC, 全称是inline formatting context,其内部的元素,在水平方向上,一个接一个地显示;在垂直方向上,每个元素可以设置不同的对齐方式;IFC内部的元素,被一行行的矩形框所包含,这些虚拟的矩形框,我们称为行框(line box)。IFC的作用区域,可以看成是包含其所有子元素的行框组成的矩形区域。

    5.4.2.2. 创建方式

    和BFC相比,它的创建方式是被动的、隐式的,是由所包含的子元素来创建:只有在一个区域内仅包含可水平排列的元素时才会生成,这些子元素可以是文本、inline-level元素或inline-block-level元素。

     

    5.4.2.3. 特性

    1. IFC内部的元素,按从左到右、从上到下的顺序排布;

    2. IFC内部的每个元素,都可以通过设置vertical-align属性,来调整在垂直方向上的对齐;

    1. 包含这些内部元素的矩形区域,形成的每一行,被称为line box(行框,后面会详细介绍);

    5.4.3. FFC和GFC

    FFC(flex formatting context)和GFC(grid formatting context),分别是flex布局和grid布局的内容,这两个模块的内容非本文介绍的重点,所以感兴趣的同学可以自行google。

    5.5. 包含块(Containing Block)

    5.5.1. 定义

    我们在设置元素尺寸属性(width、height、padding、margin和border)的百分比值或偏移属性(top、right、bottom和left)的值时,通常会有一个“相对参考系”,这个”相对参考系”一般是包裹着这个元素的块级祖先元素(一般是块级父元素)或离这个元素最近的非static(relative、absolute和fixed)定位的祖先元素。这些具有“相对参考系”作用的祖先元素,其容纳区域(cotent box或padding box),其实还有一个专门术语形容之,那就是包含块(在知识体系中有个包含块的概念,有助于加深对position定位原理的掌握)。

    特别地,relative定位元素,其尺寸属性(width、height等)的“相对坐标系”仍是其包含块(块级祖先元素(一般是父元素)的content box),但是偏移属性(top、right、bottom和left)的“相对坐标系”则是其在文档流原来的位置。

     

    5.5.2. ICB(initial containing block, 初始包含块)

    5.5.2.1. 定义

    如前面所说,任何一个元素都会有一个包含块作为设置尺寸属性和偏移属性的“相对参考系”,而对于顶层的根元素<html/>,没有任何元素包裹它,它的包含块是什么?它选取什么作为“相对参考系”?

    其实根元素<html/>是有包含块的,它是一个不可见的矩形框,W3C组织称之为ICB(initial containing block, 初始包含块)。以下是W3C组织对ICB对定义:

     

    The containing block in which the root element lives is a rectangle called the initial containing block.

    5.5.2.2. ICB的尺寸和起始位置(左上角坐标)

    在解释ICB的尺寸和起始位置时,在这里先简单补充一个背景知识:连续媒体(continuous media)和分页媒体(paged media)。如何理解这两个概念?在视觉阅读层面,它们是展示内容的两种呈现方式。

    连续媒体,就是采用连续展示内容的方式,它保持了展示内容显示的连续性(一页显示所有内容),我们可以在连续媒体的viewport(可视窗口)查看当前呈现的内容。特别地,浏览器窗口就可以看成是连续媒体,当内容的尺寸超过viewport时,读者可以通过平滑滚动的方式来阅读内容。

    分页媒体,就是采用切页展示内容的方式,它将要展示的内容切分为等尺寸的多页(分页显示所有内容),我们可以在分页媒体的page area(页面显示区域)查看当前呈现的内容。特别地,像幻灯片、电子书阅读器,就可以看成是分页媒体,当内容的尺寸超过page area时,读者可以通过切页的方式来阅读内容;

    对于属于连续媒体(continuous media)的浏览器窗口来说,ICB的尺寸为viewport(浏览器视窗),其起始位置为画布原点(canvas origin,即首屏的左上角,浏览器渲染数据后生成的内容文档可以看成是一张画布)。

    对于分页媒体来说,ICB的尺寸为page area(关于ICB在分页媒体的起始位置,没有找到相关资料,但这个对于本文来说也不是重点)。

    直观来看,根元素<html/>的包含块ICB,就是“首屏”。

    5.5.3. 不同定位元素分别对应的包含块

    • static和relative定位元素的包含块,为其块级祖先元素(通常是块级父元素)的content box;
    • absolute定位元素的包含块,为最近的非静态定位祖先元素的padding box,查无非静态定位祖先元素,那么它的包含块是ICB(即根元素<html/>的包含块);
    • fix定位元素的包含块,为当前viewport(视窗);

    在这里要强调的一点,ICB(初始包含块)是专有名词,它特指根元素<html/>的包含块。不要将一个元素的初始包含块,错误理解为它的父元素。MDN的一位编辑者也犯了这种错误。具体如下:

     

    新葡亰496net 12

    经修正后:

    新葡亰496net 13

    也有一些权威CSS书籍说,当一个绝对定位元素找不到最近的非static祖先元素时,则相对于根元素<html/>定位,这种说法也是不严谨的。刚好看到一本,如下:

     

    新葡亰496net 14

    图1. 书的封面

    新葡亰496net 15

    图2. 原话截图

    我们可以通过一个简单的例子推翻这种说法:将根元素html的高度设置为超过viewport高度,如5000px(假设viewport高度为500px),再将一个没有最近的非static祖先元素的绝对定位元素的bottom设置为0,尺寸为100px*100px即可。如果真如该书中所言,那么在首屏时,该绝对定位元素是被隐藏在滚动条下面的。而实际情况是:该绝对定位元素必然出现在首屏的底端,并且会随着页面滚动而滚动。验证如下:

     

    新葡亰496net 16

    相信这个谬误在前端圈流传已久,希望各位同学引起重视。

    5.6. 基本原理

    5.6.1. 文档流(正常流)

    5.6.1.1. 定义

    关于“文档流”,并没有找到较为官方的定义。笔者从google搜到一些认为比较靠谱的解释,罗列如下:

    1)摘自:《CSS: understanding the document flow》

     

    The document flow is the model by which elements are rendered by default in the CSS specifications. In this model, elements are rendered according by their default display rule. In other words, block-level elements are displayed on a new line and inline elements on the same line. Everything is stacked in an ordered way from top to bottom. 

    2)摘自:《What is “document flow”?》

    Document flow is the arrangement of page elements as defined by positioning statements and the order of html statements; that is, how the different elements take up space and arrange themselves around each other.

    在这里我想分享一下我自己对“文档流”下的定义:

     

    文档流,是页面元素默认存放的“容器”。

    5.6.1.2. 特性

    文档流具有如下特性:

    1. 文档流按照页面元素书写的顺序,将页面元素按从左到右,从上至下的一般顺序进行排列,而页面元素则根据自身的布局属性(block-box or inline-box),决定是行内显示,还是换行显示;

    1. 文档流内的元素,相互尊重:有序排列,彼此识别;

    5.6.1.3. 脱离文档流

    元素脱离文档流,按我之前下的定义,其实就意味着:元素脱离了默认存放的容器,换到另外一个容器存放。一个元素脱离了文档流,这样会导致:其父元素无法识别其,其也不参与父元素高度的计算。若有一个父元素的所有子元素都脱离文档流,则会出现“高度塌陷”问题。常见的脱离文档流的方法有:

    • 将元素设置为浮动元素
    • 将元素设置为absolute、fixed元素

    5.6.2. 浮动(float属性)

    5.6.2.1. 浮动元素的分类

    根据float属性的设置,元素可以分为浮动元素(值为left或right)和非浮动元素(值为none)。而按浮动方向划分,又可细分为:

    • 左浮动元素:float值为left的元素
    • 右浮动元素:float值为right的元素

     

    5.6.2.2. 浮动原理

    要想掌握浮动元素的浮动原理,只要理解浮动元素的浮动起始位置、浮动方向和浮动结束位置即可。

    • 浮动起始位置浮动元素(包括左右)的浮动起始位置,为最后一行最左侧的空白位置,而不管空白位置是否能够容纳当前浮动元素;
    • 浮动方向左浮动元素的浮动方向为从起始位置向左浮动;右浮动元素的浮动方向为从起始位置向右浮动;
    • 浮动结束位置

    左浮动元素遇到第一个左浮动元素或包含块的最左侧padding时,结束浮动;

    右浮动元素遇到第一个右浮动元素或包含块的最右侧padding时,结束浮动;

    以下demo可以帮助各位同学理解浮动元素的三要素:

    /06:layout/float/1. 浮动元素三要素.html:

    <body> <div class="fl">左浮动元素-1(width: 30%; height: 100px;)</div> <div class="fl">左浮动元素-2(width: 30%; height: 200px;)</div> <div class="fl">左浮动元素-3(width: 30%; height: 100px;)</div> <div class="fl">左浮动元素-4(width: 30%; height: 100px;)</div> </body>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <body>
     
        <div class="fl">左浮动元素-1(width: 30%; height: 100px;)</div>
     
        <div class="fl">左浮动元素-2(width: 30%; height: 200px;)</div>
     
        <div class="fl">左浮动元素-3(width: 30%; height: 100px;)</div>
     
        <div class="fl">左浮动元素-4(width: 30%; height: 100px;)</div>
     
    </body>

    显示结果:

    新葡亰496net 17

    说明:

     

    a. 有四个连续左浮动的元素,每个元素宽度为30%;

    b. 当一行排满三个元素时,当前行只剩10%的宽度,不足以容纳第四个左浮动元素;

    c. 第四个浮动元素,从起始位置(最后一行的最左侧空白)开始向左浮动,直到遇到第二个浮动元素的边界;

    为了帮助大家理解好浮动原理,在这里我想额外定义几个术语:

    • 左浮动队:由若干个连续的左浮动元素组成
    • 右浮动队:由若干个连续的右浮动元素组成
    • 左浮动队头元素:左浮动队的第一个元素,也是最左侧的元素
    • 右浮动队头元素:右浮动队的第一个元素,也是最右侧的元素

    特别地,

    1. 同一行内,最多有两条浮动队,一是左浮动队,二是右浮动队;

    2. 同一行内,一条浮动队可能占满一行;

    3. 连续浮动的若干元素,如果无法在同一行内显示,则会按行被切分为两条或更多条浮动队;

     

    5.6.2.3. 浮动对元素display的影响

    当元素设置为浮动元素后,可能会引发display属性的值变化,具体规则如下:

     

    新葡亰496net 18

    浮动元素display属性变化对照表

    5.6.3. 清除浮动(clear属性)

    5.6.3.1. 三要素

    清除浮动,其作用是改变“当前元素”与“前一个声明的浮动元素”之间的默认布局规则,这种改变主要体现为:让当前元素换行显示。这句话包含三个要素,分别为:

    • 使用者:当前元素(浮动元素或者非浮动元素的块级元素)
    • 作用对象(清除谁的浮动):前一个声明的浮动元素
    • 目的(作用):让当前元素换行显示

    特别地,为什么使用者不包括非浮动的inline元素?因为非浮动的inline元素能够识别浮动元素,是否使用clear清除“前一个声明的浮动元素”的浮动,其布局结果是一样的。感兴趣的同学可以参考:06:layout/clear/4.非浮动inline元素清除左浮动.html,可以在调试器中观察注释非浮动inline元素的clear:left前后,其显示位置的变化。而非浮动的块级元素,因为无法识别前面声明的左浮动元素,故会和左浮动元素发生重叠(左浮动元素在上),所以非浮动的块级元素使用clear:left清除前一个左浮动元素,就能避免重叠的现象。

    5.6.3.2. clear属性的取值及应用场景

    前面简单介绍了clear属性的作用,是清除前面声明的浮动元素的浮动,然后让当前元素换行显示。但是要具体怎么使用,我们还得深入到clear的属性值和应用场景。

    clear属性的取值有left、right和both。那么它们的应用场景分别是什么?

    left值的应用场景是,前面声明的浮动元素是向左浮动(float: left);

    right的应用场景是,前面声明的浮动元素是向右浮动(float: right);

    both的应用场景是,前面声明的浮动元素的浮动方向不确定,可能是左,也可能是右(了解过clearfix实现原理的同学,就不难明白);

    再次强调一下,当前元素如果要清除浮动,清除的是前面声明的浮动元素的浮动,其clear属性要取什么值,跟当前元素的是否是浮动元素或浮动方向没有任何关系,而取决于其前面声明的浮动元素的浮动方向。

    举个例子,一个右浮动元素(float:right),前面有一个左浮动元素(float:left),如果这个右浮动元素使用clear: left时,这个元素会清除前一个元素的浮动,进而换行显示;如果使用clear:left时,这个元素在当前行的最右端显示。如下图所示(06:layout/clear/4.右浮动清除左浮动.html):

     

    新葡亰496net 19

    图1. 右浮动清除左浮动

     

    新葡亰496net 20

    图2.右浮动元素清除右浮动

    在了解完clear属性的取值和应用场景,我们可以对其作用,可以总结为:

    如果当前元素浮动元素或非浮动的块级元素,且前面声明的元素是左(右)浮动元素,那么当前元素可以使用clear: left(clear: right),清除前一个左(右)浮动元素的左(右)浮动,此时当前元素会换行显示;如果当前元素clear的浮动与前面一个浮动元素的浮动方向不同向,当前元素不会换行;

     

    5.6.3.3. 清除浮动后的margin合并问题

    1)两个浮动元素之间,其垂直方向上的margin不会发生合并,如下图所示:

    新葡亰496net 21

                                        浮动元素间会不发生垂直margin合并

    2)非浮动的块级元素和浮动元素之间,其垂直方向上的margin会发生合并,如下图所示:

     

    新葡亰496net 22

    非浮动的块级元素与浮动元素间会发生margin合并

    特别地,MDN的文档说非浮动的块级元素与浮动元素间不会发生margin合并,实际上会,上述结果已经证明,已在MDN上更正该错误。以下为MDN未修改前的原话:

     

     

                                                       MDN未修正前的原话

    5.6.3.4. 清除浮动的特殊应用:解决父元素高度塌陷问题

    众所周知,当一个父元素里面的所有元素都是浮动元素时,此时父元素无法识别这些浮动子元素,会进一步导致父元素发生高度塌陷问题。一种通用的解决方案就是在父元素内部的尾部append一个非浮动的、尺寸为0的块级元素(后面简称fix元素),然后使用clear:both,让这个fix元素换行显示,进而让父元素能够识别前一行的高度。这种朴素的方案其实就是clearfix的基本原理,clearfix只是更加优雅地用:after来实现fix元素。

    特别说明:解决父元素高度塌陷问题,还可以通过将父元素声明为BFC元素来实现。

    5.6.4. 定位(position属性)

    5.6.4.1. 定位元素的分类

    根据position属性的取值,static(默认值)、relative、absolute、fixed,元素可以分为静态定位元素(值为static)、相对定位元素(值为relative)、绝对定位元素(值为absoute)和固定定位元素(值为fixed)。

    注:position的取值还有sticky,但IE11都不支持,此处不讲

    5.6.4.2. 定位原理

    static定位元素定位时的相对坐标系:无法设置top、right、bottom和left这四个偏移属性;

     

    relative定位元素定位时的相对坐标系:元素在文档流原来的位置(区域);

    absolute定位元素定位时的相对坐标系:离元素最近的一个非static(包含relative、absolute和fixed)定位祖先元素(包含块为其padding box),如果没有则为ICB(初始包含块),即根元素html的包含块;

    fixed定位元素定位时的相对坐标系:当前的视窗(viewport);

    5.6.5. line box(行框)

    5.6.5.1. 定义

    前面在介绍IFC时,我们提到过line box的定义:包含IFC内部的所有子元素的虚拟矩形区域,形成的每一行,称为line box。由于它是矩形的,中文常见将之翻译为行框。

    5.6.5.2. 模型结构(七线谱)

    line box的模型结构,形如七线谱,其中有六条重要的线:top线、text-top线、middle线、baseline线、text-bottom线和bottom线,如下图所示:新葡亰496net 23

     

    行框七线谱

    其中top线到text-top线的区域bottom线到text-bottom的区域,又称为行半距(half-leading),两个行半距之和,为一个行距;text-top线到text-bottom线的区域,称之为内容区域(content-area)。如下图所示:

     

    新葡亰496net 24

    行框区域划分

    5.6.5.3. 行框高度的计算

    行框的高度,即一行的top线和bottom线间的垂直距离,这个垂直距离为:上下两个行半距的高度和一个内容区域的高度之和。影响行框高度计算的因素来自两方面,一是自身line-height属性的设置,二是内部inline-level子元素的margin box高度的取值和line-height、vertical-align两个属性的设置。关于其计算规则,具体罗列如下:

    1. 一个元素的行框高度,可由该元素的line-height属性设置;

    2. 一个元素的行框高度,受不可置换(span、a、label等)的子元素的内容高度(text-top到text-bottom的垂直距离)影响(内容高度又受font-size属性和浏览器的解析规则影响,但主要由font-size决定;相同的font-size,在不同的浏览器,计算出来的内容高度也不一样,最终导致的行框高度也不一样);

    3. 一个元素的行框高度,可由不可置换(span、a、label等)的子元素的line-height属性设置;

    4. 一个元素的行框高度,可由可置换行内元素(如img)或display属性为inline-block、inline-table的这一类inline-block-level子元素的margin box高度和vertical-align属性决定,当vertical-align为top或bottom时,行框的高度达到最小,刚好为子元素的margin box高度;

     

    新葡亰496net 25

    图1. img元素的margin box高度比行框高度小

    新葡亰496net 26

    图2. img元素的margin box高度与行框高度一致,行框高度达到最小

    1. 如果同时满足以上设置条件,那么行框的高度取最大值;

    友情提示:在图1 img元素的margin box高度比行框高度小,我们会看到img元素到父元素的底端会有一段空白,为什么会有这种现象?张鑫旭老师在《CSS深入理解vertical-align和line-height的基友关系》一文中将之定义为“幽灵空白节点”,其实结合行框理论来解释,这段空白并不“幽灵”,也很好理解:它是行框的baseline线到bottom线的垂直距离,可置行内换元素如img和inline-block-level元素,在被浏览器解析时,会和“文本”一样,默认在baseline线上显示,而不是在行框的bottom线上。

    举个例子:  行框高度的计算

    html:

    <style> .line-box { background: yellow; line-height: 32px; font-size: 20px; } .span-1 { line-height: 40px; background: red; } .span-2 { line-height: 38px; background: green; } img { width: 50px; height: 50px; } </style> <body> <div class="line-box"> <span class="span-1">span(line-height: 40px)</span> <span class="span-2">span(line-height: 38px)</span> </div> <div class="line-box"> <span class="span-1">span(line-height: 40px)</span> <span class="span-2">span(line-height: 38px)</span> <img src="#" /> </div> </body>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    <style>
     
            .line-box {
     
            background: yellow;
     
            line-height: 32px;
     
            font-size: 20px;
     
            }
     
            .span-1 {
     
            line-height: 40px;
     
            background: red;
     
            }
     
            .span-2 {
     
            line-height: 38px;
     
            background: green;
     
            }
     
            img {
     
            width: 50px;
     
            height: 50px;
     
            }
     
    </style>
     
    <body>
     
        <div class="line-box">
     
            <span class="span-1">span(line-height: 40px)</span>
     
            <span class="span-2">span(line-height: 38px)</span>
     
        </div>
     
        
     
        <div class="line-box">
     
            <span class="span-1">span(line-height: 40px)</span>
     
            <span class="span-2">span(line-height: 38px)</span>
     
            <img src="#" />
     
        </div>
     
    </body>

    显示结果(chrome下):

     

    新葡亰496net 27

    图1. line box内部仅有不可置换元素

     

    新葡亰496net 28

    图2. line box内部还有可置换元素img

    说明:

    a. 元素每一行的line-height,既可以由当前元素的line-height属性设置(32px),也可以由该行子元素的line-height属性设置(分别是40px和38px),但取最大的line-height(40px),如图1所示;

    b. 特别地,如果一行内还有可以设置height的可置换元素如img(height: 50px),且img的高度大于设置的最大line-height(40px)时,那么该行会被撑高,浏览器会重新计算line-height(最终结果为63px),如图2所示;

    5.6.5.4. 与line box行框有关的两个重要属性:line-height和vertical-align

    相信很多前端同学有这样的感觉:line-height和vertical-align这两个属性总是形影不离,而且有着一种说不清的关系。

    它们到底有什么联系吗?

    其实这两个属性的关系可由行框和行框内的inline-level元素来体现。line-height属性决定inline-level元素所在行框的高度,它是inline-level元素在一行内垂直方向上的显示范围;vertical-align属性则决定inline-level元素在一行内的垂直对齐方式,即决定inline-level元素在一行内垂直方向上的最终位置。下面我们来深入介绍这两个属性:

    1)line-height属性

    1.1)line-height属性的作用

    line-height属性一般用于块级元素设置其内部每一行的高度,即默认行高;line-height属性也可以用于不可置换元素(如span、a)设置所在行框的高度。也就说,每一行计算出来的最终行高,既受父元素line-height属性的影响,也受子元素line-height属性的影响。

    1.2)line-height属性的取值

    line-height的取值有<length>、<number>、和关键字normal(默认值)。其中:

    • <length>表示使用指定带单位的长度来设置line-height,这些长度单位可以是px、pt和em和rem;
    • <number>表示用font-size值的倍数来设置line-height;
    • <percentage>表示用font-size值的百分比来设置line-height;
    • 而关键字normal,其最终计算出来的尺寸,则取决于浏览器各自的解析机制和选用的font-family类型:浏览器会根据选用的font-family类型来计算出一个合适的值,W3C官方推荐使用<number>值,并且推荐值的范围为1.0到1.2之间(但经过实测,浏览器在实现时,远比这个复杂,而且不同浏览器间也存在差异。唯一可以确定的一点是,最终的行高肯定会比font-size值要大)。

     

    我们在将UI稿实现为页面代码时,常常强调要Pixel Perfect、高精准地还原设计稿。但

    我们常常会遇到这样一个问题:当我们用一个块级元素包裹文本时,会发现块级元素的高度,实际比文本的font-size尺寸还要高,导致上下形成了一些空白,进一步造成块级元素内的文本与垂直方向上相邻元素的距离变大,如下图所示:

     

    新葡亰496net 29

    line-height值为normal

    这种误差是由于line-height的默认值为normal,那有什么办法可以解决这个问题呢?较常用的方法是将块级元素的line-height设置为1或100%。设置后的结果如下图所示:

    新葡亰496net 30

     

    line-height值为1或100%

    这样做也有一点不好,那就是:浏览器最终解析出来的内容高度,有可能是比font-size要大的,当行高为font-size时,文本内容就会溢出。我们将字体放大为100px,溢出效果就很明显,如下图所示:

     

    新葡亰496net 31

     

    line-height值为1或100%

    1.2)line-height属性对元素高度的影响

    我们可以通过了解line-height属性分别对块元素和不可置换的行内元素自身高度的影响、以及不可置换的子元素的line-height属性对父元素高度的影响,来深入理解line-height属性的作用。

    为了帮助大家更好地理解line-height,我设计了如下三个小demo:

     

    demo1:  line-height属性对块级元素自身height的影响

    html:

    </body> // div为单行 <div class="block"> div(line-height: 32px) </div> // div为多行 <div class="block"> div(line-height: 32px) <br> div(line-height: 32px) </div> </body>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    </body>
     
        // div为单行
     
        <div class="block">
     
            div(line-height: 32px)
     
        </div>
     
        
     
        // div为多行
     
        <div class="block">
     
            div(line-height: 32px)
     
            <br>
     
            div(line-height: 32px)
     
        </div>
     
    </body>

    显示结果(chrome下):

     

    新葡亰496net 32

    图1. div为单行时

     

    新葡亰496net 33

    图2. div为多行时

    说明:

    a. 当一个块元素不设置height,而且这个块元素仅有一行时,那么其height刚好等于line-height;

    b. 当一个块元素不设置height,而且这个块元素有多行时,那么其height刚好等于每一行的line-height之和;

     

    demo2:  line-height属性对不可置换行内元素(如span)的height的影响

    html:

    <div class="line-box"> <span class="inline-element"> span(line-height: 40px;font-size: 20px) </span> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <div class="line-box">
     
        <span class="inline-element">
     
            span(line-height: 40px;font-size: 20px)            
     
        </span>
     
    </div>

    显示结果:

    新葡亰496net 34

     

    图1. 行内元素为单行时,height为28px(在chrome下)

    新葡亰496net 35

     

    图2. 行内元素单行时,height为20.5px(在firefox下)

    说明:

     

    a. 不可置换行内元素为单行时,其height等于text-top线到text-bottom线的距离,所以line-height的取值不会影响到其height,其height由font-size和浏览器的默认解析机制决定(一般>font-size,大多少则取决于浏览器解析机制,如图1、2所示);

    b. 不可置换元素为多行时,其height等于第一行的text-top线到最后一行的text-bottom线的距离,此时line-height的取值就会影响到其height,其height=line-height * 行数 – (line-height – 每一行text-top线到text-bottom的距离),即height=line-height * 行数 – 2 * half-heading;如下图所示:

     

     

    新葡亰496net 36

    图3. 行内元素为多行时(在chrome下)

    demo3:不可置换的子元素(如span)的line-height,对父元素height的影响

     

    html:

    <div class="line-box"> <span class="span-1">span(line-height: 40px)</span> <span class="span-2">span(line-height: 38px)</span> <br> <span class="span-3">span(line-height: 50px)</span> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <div class="line-box">
     
          <span class="span-1">span(line-height: 40px)</span>
     
          <span class="span-2">span(line-height: 38px)</span>
     
           <br>
     
           <span class="span-3">span(line-height: 50px)</span>
     
    </div>

    显示结果(chrome下):

    新葡亰496net 37

    说明:

    a. 块级元素每一行的行高都可以不同;

    b. 不可置换的行内子元素的line-height属性,可以决定所在行框的高度;

    c. 如果一个父元素不设置height,那么其height为所有行的高度之和;

    d. 不可置换的行内子元素的line-height属性,是通过影响行框的高度来影响父元素的高度的。

    2)vertical-align属性

    vertical-align的作用之一:就是用于设置inline-level元素自身在“行框”内的垂直对齐方式,其控制范围在一行内。较常用的值有top、middle、baseline(默认值)和bottom,不常用的有text-top、text-bottom、sub和super,几乎不用的有<length>和<percentage>。

    新葡亰496net 38

    行框七线谱

    vertical-align属性的几个重要取值的作用如下:

    • 当vertical-align取top时,表示当前inline-level元素的上margin edge在行框内贴顶;

     

    • 当vertical-align取bottom时,表示当前inline-level元素的下margin edge在行框内贴底;

     

    • 当vertical-align取middle时,表示当前inline-level元素的垂直平分线和行框的middle线重合;

     

    • 当vertical-align取baseline时,表示当前inline-level元素的下margin edge紧贴在行框的baseline上;

    vertical-align属性的另一个作用:就是table-cell元素用于控制其内部子元素在垂直方向上的对齐方式,而且这些子元素的类型不受限制,不仅可以是span,而且可以是div。

    举个例子:

    html:

    <table> <tr> <td class="top">div(top)</td> <td class="middle">div(middle)</td> <td class="baseline">div(baseline)</td> <td class="bottom">div(bottom)</td> </tr> </table>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <table>
     
        <tr>
     
            <td class="top">div(top)</td>
     
            <td class="middle">div(middle)</td>
     
            <td class="baseline">div(baseline)</td>
     
            <td class="bottom">div(bottom)</td>
     
        </tr>
     
    </table>

    显示结果:

    新葡亰496net 39

    说明:

    a. table-cell元素通过设置自身的vertical-align属性,来设置其子元素在垂直方向上的对齐方式;

    特别说明:我们常用说的使用table布局来实现子元素在父元素内部垂直居中,就是运用到了这个知识点。

    5.6.6. margin

    在传统的布局方案中,margin不仅用来隔离自身与相邻元素或父元素(一般不推荐用来隔离父元素),而且在元素水平和垂直方向上的居中定位,亦发挥了重要的作用。下面我们来深入介绍margin的相关布局特性。

    5.6.6.1. auto的计算规则(在width和margin上使用)

    1)水平方向上

    谈到“如何设置文档流中的块级元素在父元素内部水平居中?”这个布局问题,相信很多同学马上会想到这个方案:给元素设置固定宽度,并使用margin: 0 auto(水平方向上的margin为auto)

     

    .child {

    width: 100px;

    margin: 0 auto;

    }

    它的实现原理,包含如下四个基础知识点:

    • 块级元素的水平尺寸(outerWidth,margin box的宽度)的计算规则:

    outerWidth = margin-left border-left-width padding-left width padding-right border-right margin-right,如下图所示:

    新葡亰496net 40

    • 文档流中的块级元素,其在水平方向上的尺寸属性的初始值,仅width为auto,其余为0
    • 在水平方向上的尺寸属性,仅width、margin-left和margin-right可以设置auto值(自动计算)
    • 文档流中的块级元素,其在水平方向上的尺寸属性,当值为auto时,表示取所在行的剩余宽度,特别地,当margin-left和margin-right的值均为auto时,会平分所在行的剩余宽度

     

     

    在理解了上述四个基础知识点,我们不难理解其原理:

    当块级元素在水平方向上的尺寸属性,除了margin-left和margin-right值为auto,其余皆为定值,那么margin-left和margin-right会自动平分父元素的剩余宽度,进而达到在父元素内部水平居中的效果,如下图所示:

     

    新葡亰496net 41

      图1. 元素在父元素内部水平居中(左右margin各取一半)

    新葡亰496net 42

    图2. 水平居中元素的盒模型解构图

    结合上述四个基础知识点,我们还可以得出如下结论:

     

    文档流中的块级元素如果不设置任何水平尺寸属性,那么其默认的width为当前行的content width,此时width取auto和100%,最终的计算值一样

    2)垂直方向上

    或许我们都曾问过这样的一个问题:既然可以通过设置margin: 0 auto,让文档流中的块级元素在父元素内部水平居中,那么可否通过设置margin: auto 0,让其垂直居中?

    答案是不能的,因为文档流中的块级元素,其垂直方向上的margin为auto时的计算规则和在水平方向上的计算规则不同:不取父元素剩余的高度,而为0。W3C标准原话如下:

     

    “If “margin-top” or “margin-bottom” is “auto”, their used value is 0″

    或许大家会问,为什么要这样设计呢?官方并没有给出说明,但是有网友给出了如下几个解释,罗列如下,供各位参考(可以在留言中分享你的看法,本人比较认同第一条):

     

    It could be because of the typical vertical page flow, where page size increases height-wise. So, centering an element vertically in its container is not going to make it appear centered, relative to the page itself, unlike when it’s done horizontally (in most cases).

    And maybe it’s because of this same reason, they decided to add an exception for absolute elements which can be centered vertically along the entire page’s height.

    It could also be because of the margin collapse effect (a collapse of adjacent elements” margins) which is another exception for the vertical margins.

    在W3C标准规约中,虽不能使用margin: auto 0,实现普通文档流中的块级元素在父元素内部垂直居中,但是可以使用margin: auto 0,实现绝对或固定定位元素在包含块内部垂直居中,因为绝对或固定定位元素垂直方向上的margin,其 auto仍会取包含块的剩余高度,W3C官方文档给出的计算公式如下:

    ‘top’ ‘margin-top’ ‘border-top-width’ ‘padding-top’ ‘height’ ‘padding-bottom’ ‘border-bottom-width’ ‘margin-bottom’ ‘bottom’ = height of containing block

    等价的简化公式:

    子元素outerHeight  = 包含块height  – 子元素top – 子元素bottom

    提示:

    1. 子元素outerHeight,是指当前子元素margin box的高度;

    2. 包含块height,可以为当前子元素的相对定位参考系元素的padding box的高度、ICB的高度或viewport的高度;

    要使用上述规则来实现子元素在父元素内部垂直居中,那么就需要保证:

    • 子元素的top值 bottom值为0(原因:让子元素outerHeight 等于包含块height)
    • 子元素的top值取0(原因:让子元素的上margin edge紧贴包含块的顶部)

    下面通过一个demo来详细介绍:

    html:

    <style> .parent { position: relative; background: yellow; height: 100px; } .child { position: absolute; top: 0; bottom: 0; background: green; width: 140px; height: 20px; margin: auto 0; text-align: center; } </style> <body> <div class="parent"> <div class="child">垂直居中的子元素</div> </div> </body>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
      <style>
     
            .parent {
     
                position: relative;
     
                background: yellow;
     
                height: 100px;
     
            }
     
            .child {
     
                position: absolute;
     
                top: 0;
     
                bottom: 0;
     
                background: green;
     
                width: 140px;
     
                height: 20px;
     
                margin: auto 0;
     
                text-align: center;
     
            }
     
    </style>
     
    <body>
     
        <div class="parent">
     
        <div class="child">垂直居中的子元素</div>
     
        </div>
     
    </body>

    显示结果:

    新葡亰496net 43

    图1. 子元素在父元素内部垂直居中(上下margin各取一半)

    新葡亰496net 44

    图2. 垂直居中元素的盒模型解构图

    说明:

    a. 绝对定位的子元素的top为0,其轮廓(包含margin)的上边界与其包含块内容区域的顶部紧贴;

    b. 由已知求未知:包含块的height已知,子元素的top值和bottom值之和为0,即子元素的outerHeight可求,又因为子元素height已知,故垂直方向上的剩余高度可以确定,当子元素的margin-top和margin-bottom均为auto时,将平分剩余的高度;

     

    5.6.6.2. margin合并(margin collapsing)

    在垂直方向上,元素与自身或相邻的兄弟元素、父元素、子元素的margin,会发生合并(注意:在IE6/7子元素垂直方向上的margin会隔离父元素,而不是和父元素的margin发生合并,IE8 则与标准浏览器同),margin取较大的值,而在水平方向上则不会。各位读者可以从下面三个demo,来理解垂直方向上margin的合并:

     

    1) 父元素与子元素(第一个子元素、最后一个子元素)

    html:

    <div class="wrapper"> <div class="parent"> <div class="child first-child">第一个子元素(margin-top: 20px)</div> <div class="child last-child">最后一个子元素(margin-bottom: 20px)</div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <div class="wrapper">
     
      <div class="parent">
     
        <div class="child first-child">第一个子元素(margin-top: 20px)</div>
     
        <div class="child last-child">最后一个子元素(margin-bottom: 20px)</div>
     
      </div>
     
    </div>

    显示结果:

    新葡亰496net 45

    说明:

     

    a. 父元素(黄色)的margin-top(40px)和第一个子元素的margin-top(20px)发生融合(取较大的40px);

    b. 父元素(黄色)的margin-bottom(40px)和最后一个子元素的margin-bottom(20px)发生融合(取较大的40px);

    2) 上下相邻的兄弟元素(同层元素)

     

    html:

    <div class="parent"> <div class="child first-child">第一个元素(margin-bottom: 40px)</div> <div class="child last-child">第二个元素(margin-top: 20px)</div> </div>

    1
    2
    3
    4
    5
    6
    7
    <div class="parent">
     
        <div class="child first-child">第一个元素(margin-bottom: 40px)</div>
     
        <div class="child last-child">第二个元素(margin-top: 20px)</div>
     
    </div>

    显示结果:

     

    新葡亰496net 46

    说明:

    a. 第一个元素的margin-bottom(40px)和第二个元素的margin-top(20px)发生融合(取较大的40px);

    3) 空块级元素

    html:

    <div class="line">第一行</div> <div class="empty-block"></div> <div class="line">第二行</div>

    1
    2
    3
    4
    5
    <div class="line">第一行</div>
     
        <div class="empty-block"></div>
     
    <div class="line">第二行</div>

    显示结果:

     

    新葡亰496net 47

    说明:

     

    a. 两行之间的空白区域,为一个空块元素;

    b. 空块的margin-top为40px, margin-bottom为20px;

    c. 两行之间的距离为40px,可知空块元素的margin-top和margin-bottom发生了合并,取较大值;

    这里我们举了三个会在垂直方向上发生margin合并的例子,但是细心的同学可能记得,我们在“5.6.3.3. 清除浮动后的margin合并问题”章节,举了一个在垂直方向上例子不会发生margin合并的例子:浮动元素间在垂直方向上不会发生margin合并。

    5.6.6.3. 子元素的margin隔离父元素

    细心的读者不难发现,在“2) 上下相邻的兄弟元素(同层元素)” 的demo可以看到

    新葡亰496net 48

    子元素(绿色)垂直方向上的margin并没有将自己与父元素(黄色)隔离开(IE6/7会,IE8 不会)。

    那么什么情况,子元素的margin可以和父元素隔离开?

    首先要强调的一点是, 子元素水平方向上的margin,始终能够隔离父元素;然而子元素在垂直方向上的margin隔离父元素的情况,本人记录的仅有以下四种(欢迎补充):

    case 1: 父元素是BFC元素

    html:

    <div class="parent"> <div class="child">子元素(margin: 20px)</div> </div>

    1
    2
    3
    4
    5
    <div class="parent">
     
        <div class="child">子元素(margin: 20px)</div>
     
    </div>

    显示结果:

     

    新葡亰496net 49

    说明:

    a. 父元素(黄色)是BFC元素,子元素(绿色)垂直方向上的margin能够隔离父元素;

    case 2:父元素拥有border

    html:

    <div class="parent"> <div class="child">子元素(margin: 20px)</div> </div>

    1
    2
    3
    4
    5
    <div class="parent">
     
        <div class="child">子元素(margin: 20px)</div>
     
    </div>

    显示结果:

     

    新葡亰496net 50

    说明:

    a. 父元素(黄色)拥有border,子元素(绿色)垂直方向上的margin能够隔离父元素;

    case 3:父元素拥有padding

    html:

    <div class="parent"> <div class="child">子元素(margin: 20px)</div> </div>

    1
    2
    3
    4
    5
    <div class="parent">
     
        <div class="child">子元素(margin: 20px)</div>
     
    </div>

    显示结果:

     

    新葡亰496net 51

    说明:

    a. 父元素(黄色)拥有padding,子元素(绿色)垂直方向上的margin能够隔离父元素;

    case 4:子元素是可置换元素或display为inline-block、inline-table、table-caption的元素

    html:

    <style> .parent { background: yellow; width: 100%; height: 60px; line-height: 20px; } .inline-block { display: inline-block; } .inline-table { display: inline-table; } img, .inline-block, .inline-table { border: 1px solid green; height: 20px; min-width: 20px; margin-top: 10px; vertical-align: top; } </style> <body> <div class="parent"> <img src="frame_image.svg" /> <div class="inline-block">display: inline-block </div> <div class="inline-table">display: inline-table</div> </div> </body>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    <style>
     
        .parent {
     
            background: yellow;
     
            width: 100%;
     
            height: 60px;
     
            line-height: 20px;
     
        }
     
        .inline-block {
     
            display: inline-block;
     
        }
     
        .inline-table {
     
            display: inline-table;
     
        }
     
        img, .inline-block, .inline-table {
     
            border: 1px solid green;
     
            height: 20px;
     
            min-width: 20px;
     
            margin-top: 10px;
     
            vertical-align: top;
     
        }
     
    </style>
     
    <body>
     
        <div class="parent">
     
            <img src="frame_image.svg" />
     
            <div class="inline-block">display: inline-block </div>
     
            <div class="inline-table">display: inline-table</div>
     
        </div>
     
    </body>

    显示结果:

     

    新葡亰496net 52

    说明:

    a. 可置换行内的和display属性为inline-block、inline-table的子元素,其垂直方向上的margin能够隔离自身与父元素;

    在这里对margin合并和margin隔离作一个小结,本人把遇到过的所有在垂直方向上会发生与不会发生margin合并、能使用margin隔离与不能使用margin隔离的例子,都罗列了出来(然而这仅仅是在标准浏览器的例子,在IE6/7情况还会不一样,但因为现在基本无需再兼容低版本的IE,所以就不再列举)。目的不是让大家记住它,而是让大家避开它:在垂直方向上,兄弟元素间尽量不要设置相邻的margin,子元素也不要使用margin来隔离父元素,这样能尽量保证你的CSS代码,在各种版本的浏览器都有较好的兼容性(显示一致)。

     

    7. 结尾语

    本文从CSS盒模型及其发展史、元素的分类及其布局特性、格式化上下文(Formatting Context)、包含块、基本原理(文档流、浮动、清除浮动、定位、行框、margin)这五大模块,系统介绍了一下前端的布局基础,希望此次分享,能够让各位读者对前端基础布局有一个底层、体系的认识。因为内容涵盖过广,难免会有纰漏,还望见谅和指正。

    此篇文章断断续续写了几个月,从年前写到年后,一方面是因为这个标题太大,含括的内容太多,需要慢慢梳理;一方面是文中要讲的东西,很多是出于本人的感悟和总结,为了保证观点的正确性、严谨性以及和行业的标准术语做好同步,需逐一验证;还有一方面也是近几个月来,本人需要处理的私事较多,分散了精力。

    时隔半年,依然有不少朋友还在关注我的公众号,谢谢你们的支持。这迟来的一篇分享,希望能对各位有用,后面我也会努力分享更多对大家有帮助的文章。

    最后我还想再分享一些心得体会:

    • 不要轻视简单的东西我们在生活中总是容易忽略一些简单的东西,因为轻视简单,导致过了几年依然也没有掌握,前端的同学更应该注意这一点。
    • 尽信书不如无书不要太相信权威,而是要学会验证、总结,并构建自己的知识体系。
    • 学技术要看官方文档很多同学在初学时喜欢看一些快速入门的教程,我觉得这种学习习惯挺好的,但是建议不要遗漏官方文档的学习。因为初学者很难去鉴定一个非官方文档的质量,运气不好的话,还会被误导。而且官方文档最贴近原作者的想法,我们更容易体会到其设计思想。

     

     

    演示代码地址:

     

    2 赞 6 收藏 1 评论

    新葡亰496net 53

    CSS魔法堂:重新认识Box Model、IFC、BFC和Collapsing margins

    2016/05/10 · CSS · BFC, Box Model, Collapsing margins, IFC

    本文作者: 伯乐在线 - ^-^肥仔John 。未经作者许可,禁止转载!
    欢迎加入伯乐在线 专栏作者。

    前言
    盒子模型作为CSS基础中的基础,曾一度以为掌握了IE和W3C标准下的块级盒子模型即可,但近日在学习行级盒子模型时发现原来当初是如此幼稚可笑。本文尝试全面叙述块级、行级盒子模型的特性。作为近日学习的记录。

    何为盒子模型?
    盒子模型到底何方神圣居然可以作为CSS的基础?闻名不如见面,上图了喂!
    新葡亰496net 54
    再来张切面图吧!
    新葡亰496net 55
    下面我们以 <div></div> 为栗子。 <div></div> 标签被浏览器解析后会生成div元素并添加到document tree中,但CSS作用的对象并不是document tree,而是根据document tree生成的render tree,而盒子模型就是render tree的节点。
    * 注意:
    * 1. CSS作用的是盒子(Box), 而不是元素(Element);
    * 2. JS无法直接操作盒子。

    盒子模型的结构
    由于块级盒子在验证效果时干扰信息更少,便于理解盒子模型,因此下面将以块级盒子模型来讲解。
    注意: 行级盒子模型与块级盒子模型结构一致,只是行级盒子在此基础上有自身特性而已。
    从上面两幅图说明盒子模型其实就是由以下4个盒子组成:

    1. content box:必备,由content area和4条content/inner edge组成;
    2. padding box:可选,由padding和4条padding edge组成。若padding宽度设置为0,则padding edge与content edage重叠;
    3. border box:可选,由border和4条border edge组成。若border宽度设置为0,则border edge与padding edage重叠;
    4. margin box:可选,由margin和4条margin/outer edge组成。若margin宽度设置为0,则margin edge与border edage重叠。
      对于刚接触CSS的同学,经常会将”通过width/height属性设置div元素的宽/高”挂在口边,其实这句话是有误的。
    5. 首先css属性width和height作用于div元素所产生的盒子,而不是元素本身;
    6. 另外盒子模型由4个盒子组成,那width和height到底是作用于哪些盒子呢?
      这里就分为IE盒子模型和标准盒子模型了。
         IE box model    
      IE5.5(怪异模式)采用IE盒子模型,其它将使用W3C标准盒子模型。
      新葡亰496net 56

    JavaScript

    width = content-width padding-width border-width height = content-height padding-height border-height

    1
    2
    width = content-width padding-width border-width
    height = content-height padding-height border-height

      Standard box model  
    新葡亰496net 57

    JavaScript

    width = content-width height = content-height

    1
    2
    width = content-width
    height = content-height

    游走于IE box model 和 Standard box model间的通道——box-sizing属性
    我们看到存在两种width/height的划分方式,到底哪种才对呢?其实两种都对,具体看如何使用而已。另外IE8开始支持CSS3属性box-sizing,让我们可以自由选择采用哪种盒子:)
    box-sizing:content-box/border-box/inherit
    content-box——默认值,采用Standard box model
    border-box——采用IE box model
    inherit——继承父元素属性值
    sample:

    CSS

    Element{ -moz-box-sizing: border-box; // FireFox3.5 -o-box-sizing: border-box; // Opera9.6(Presto内核) -webkit-box-sizing: border-box; // Safari3.2 -ms-box-sizing: border-box; // IE8 box-sizing: border-box; // IE9 ,Chrome10.0 ,Safari5.1 ,Opera10.6 }

    1
    2
    3
    4
    5
    6
    7
    Element{
      -moz-box-sizing: border-box; // FireFox3.5
      -o-box-sizing: border-box; // Opera9.6(Presto内核)
      -webkit-box-sizing: border-box; // Safari3.2
      -ms-box-sizing: border-box; // IE8
      box-sizing: border-box; // IE9 ,Chrome10.0 ,Safari5.1 ,Opera10.6
    }

    行级盒子——怀疑人生de起点:)                  
    之前我理解的盒子模型如上所述,当我看到行级盒子的种种现象时,便开始怀疑人生了:(
    width/height不起作用。。。

    CSS

    .defined-wh{ width: 100px; height: 50px; border: solid 1px red; background: yellow; }

    1
    2
    3
    4
    5
    6
    7
    .defined-wh{
      width: 100px;
      height: 50px;
     
      border: solid 1px red;
      background: yellow;
    }

    对于block-level box

    XHTML

    <div class="defined-wh"></div>

    1
    <div class="defined-wh"></div>

    新葡亰496net 58
    对于inline-level box

    XHTML

    <span class="defined-wh"></span>

    1
    <span class="defined-wh"></span>

    新葡亰496net 59
    行级盒子的宽度怎么会是0呢?高度是有的但不是50px啊,到底什么回事啊?
    原因很简单,那就是行级盒子的content box的高/宽根本就不是通过height/width来设置的。
    content box/area的高由font-size决定的;
    content box/area的宽等于其子行级盒子的外宽度(margin border padding content width)之和。

      行级盒子被挤断了。。。

    CSS

    .broken{ border: solid 1px red; background: yellow; }

    1
    2
    3
    4
    .broken{
      border: solid 1px red;
      background: yellow;
    }

    对于block-level box

    XHTML

    <div class="broken">一段文字一段文字一段文字一段文字一段文字一段文字</div>

    1
    <div class="broken">一段文字一段文字一段文字一段文字一段文字一段文字</div>

    新葡亰496net 60
    对于inline-level box

    XHTML

    <span class="broken">一段文字一段文字一段文字一段文字一段文字一段文字</span>

    1
    <span class="broken">一段文字一段文字一段文字一段文字一段文字一段文字</span>

    新葡亰496net 61
    行级盒子被五马分尸了,可怜兮兮的。更可怜的是我理解不了。。。
    其实W3C Recommendation有说明的哦!
    >The box model for inline elements in bidirectional context
    >When the element’s ‘direction’ property is ‘ltr’, the left-most generated box of the first line box in which the element appears has the left margin, left border and left padding, and the right-most generated box of the last line box in which the element appears has the right padding, right border and right margin.
    >When the element’s ‘direction’ property is ‘rtl’, the right-most generated box of the first line box in which the element appears has the right padding, right border and right margin, and the left-most generated box of the last line box in which the element appears has the left margin, left border and left padding.
    就是说当inline-level box宽度大于父容器宽度时会被拆分成多个inline-level box,
    当属性direction为ltr时,margin/border/padding-left将作用于第一个的inline-level box,margin/border/padding-right将作用于最后一个的inline-level box;若属性direction为rtl时,margin/border/padding-right将作用于第一个的inline-level box,margin/border/padding-left将作用于最后一个的inline-level box。
    看到了没?行级盒子真的会被分尸的,好残忍哦:|

    行级盒子怎么不占空间了?怎么刷存在感啊。。。

    CSS

    .existed{ margin: 20px; padding: 20px; border: solid 1px red; background: yellow; background-clip: content-box; }

    1
    2
    3
    4
    5
    6
    7
    .existed{
      margin: 20px;
      padding: 20px;
      border: solid 1px red;
      background: yellow;
      background-clip: content-box;
    }

    对于block-level box

    XHTML

    <div>before bababababababa</div> <div class="existed">babababababababababa</div> <div>after bababababababa</div>

    1
    2
    3
    <div>before bababababababa</div>
    <div class="existed">babababababababababa</div>
    <div>after bababababababa</div>

    新葡亰496net 62
    对于inline-level box

    XHTML

    <div>before bababababababa</div> <span class="existed">babababababababababa</span> <div>after bababababababa</div>

    1
    2
    3
    <div>before bababababababa</div>
    <span class="existed">babababababababababa</span>
    <div>after bababababababa</div>

    新葡亰496net 63
    看,行级盒子的margin/border/padding-top/bottom怎么均不占空间的?难道行级盒子仅有content box占空间吗?
    这里已经涉及到水平和垂直方向排版的范畴了,仅以盒子模型已无法解析理解上述的问题。
    (要结合)

    在深入解释inline-level box的上述现象前,我们需要补充一下:

    1. 一个元素会对应0~N个box;(当设置display:none;时,则对应0个box)
    2. 根据display属性值,元素会对应不同类型的controlling box(inline/block-level box均是controlling box的子类). 就CSS2而言display:inline|inline-block|inline-table|table-cell|table-column-group的元素对应inline-level box,而display:block|list-item|table|table-caption|table-header-group|table-row|table-row-group|table-footer-group的元素则对应block-level box;
    3. box布局/排版时涉及到定位问题,而CSS中通过positioning scheme来定义,其包含normal flow、floats和absolute positioning三种定位方式.而normal flow包含block formatting、inline formatting和relative positioning,其中BFC为block formatting的上下文,IFC为inline formatting的上下文。

    因此大家请注意,前方高能,前方高能!!!

    和IFC一起看inline-level box
    IFC(Inline Formatting Context),直译为“行内格式化上下文”,这是什么鬼的翻译啊?反正我对于名词一向采用拿来主义,理解名词背后的含义才是硬道理。
    我们简单理解为每个盒子都有一个FC特性,不同的FC值代表一组盒子不同的排列方式。有的FC值表示盒子从上到下垂直排列,有的FC值表示盒子从左到右水平排列等等。而IFC则是表示盒子从左到右的水平排列方式,仅此而已(注意:一个盒子仅且仅有一个FC值)。而inline-level box的FC特性值固定为IFC
    另外仅处于in-flow的盒子才具有FC特性,也就是positioning scheme必须为Normal flow的盒子才具有FC特性。
    除了IFC外,对于inline-level box排版而言还有另一个重要的对象,那就是line box。line box是一个看不见摸不着的边框,但每一行所占的垂直高度其实是指line box的高度,而不是inline-level box的高度。
      line box的特点:

    1. 同一行inline-level box均属于同一个line box;
    2. line box高度的计算方式()
      >The height of each inline-level box in the line box is calculated. For replaced elements, inline-block elements, and inline-table elements, this is the height of their margin box; for inline boxes, this is their ‘line-height’.
      >The inline-level boxes are aligned vertically according to their ‘vertical-align’ property. In case they are aligned ‘top’ or ‘bottom’, they must be aligned so as to minimize the line box height. If such boxes are tall enough, there are multiple solutions and CSS 2.1 does not define the position of the line box’s baseline.
      >The line box height is the distance between the uppermost box top and the lowermost box bottom.

    CSS

    .parent{ line-height: 1; font-size: 14px; border: solid 1px yellow; } .child{ font-size: 30px; vertical-align: middle; border: solid 1px blue; } .inline-block{ display: inline-block; overflow: hidden; border: solid 1px red; } .other{ border: solid 1px green; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    .parent{
      line-height: 1;
      font-size: 14px;
      border: solid 1px yellow;
    }
    .child{
      font-size: 30px;
      vertical-align: middle;
      border: solid 1px blue;
    }
    .inline-block{
      display: inline-block;
      overflow: hidden;
      border: solid 1px red;
    }
    .other{
      border: solid 1px green;
    }

    XHTML

    <span class="parent"> <span class="child"> <span class="inline-block">display:inline-block元素</span> xp子元素的文字 </span> xp父元素的文字 </span> <div class="other">其他元素</div>

    1
    2
    3
    4
    5
    6
    7
    8
    <span class="parent">
      <span class="child">
        <span class="inline-block">display:inline-block元素</span>
        xp子元素的文字
      </span>
      xp父元素的文字
    </span>
    <div class="other">其他元素</div>

    新葡亰496net 64

    1. 根据规则,span.parent所在行的line box的高度受span.parent、span.child、span.inline-block元素对应的inline-level box”高度”的影响。其中span.parent的”高度”为其line-height实际值,span.child的”高度”为其line-height实际值,而span.inline-block的”高度”为其margin box的高度。由于设置line-height:1,因此span.parent和span.child的content box高度等于line-height实际值;
      2. 根据vertical-align属性垂直对齐,造成各“高度”间并不以上边界或下边界对齐;
    2. span.inline-block红色的上边框(border top)到span.child蓝色的下边框(border bottom)的距离再减去1px即为line box的高度。(line box的下界其实是span.child的content box的下限的,你看”其他元素”的上边框不是和span.child的下边框重叠了吗?如果那是line box的下界,那怎会出现重叠呢)

    这里又涉及到另一个属性vertical-align了,由于它十分复杂,还是另开文章来叙述吧!

                          行级盒子小结                          
    *就盒子模型而言***

    1. inline-level box与block-level box结构一致;
    2. content box的高度仅能通过属性font-size来设置,content box的宽度则自适应其内容而无法通过属性width设置;
    3. 当inline-level box的宽度大于containing block,且达到内容换行条件时,会将inline-level拆散为多个inline-level box并分布到多行中,然后当属性direction为ltr时,margin/border/padding-left将作用于第一个的inline-level box,margin/border/padding-right将作用于最后一个的inline-level box;若属性direction为rtl时,margin/border/padding-right将作用于第一个的inline-level box,margin/border/padding-left将作用于最后一个的inline-level box。

    *垂直排版特性***
    inline-level box排版单位不是其本身,而是line box。重点在于line box高度的计算。

    1. 位于该行上的所有in-flow的inline-level box均参与该行line box高度的计算;(注意:是所有inline-level box,而不仅仅是子元素所生成的inline-level box)
    2. replaced elements, inline-block elements, and inline-table elements将以其对应的opaque inline-level box的margin box高度参与line box高度的计算。而其他inline-level box则以line-height的实际值参与line box高度的计算;
    3. 各inline-level box根据vertical-align属性值相对各自的父容器作垂直方向对齐;
    4. 最上方的box的上边界到最下方的下边界则是line box的高度。(表述不够清晰,请参考实例理解)

    Collapsing margins                      
    大家必定听过或遇过collapsing margins吧,它是in-flow的block-level box排版时的一类现象。说到排版那必须引入另一个FC特性值——BFC(Block Formatting Context)的。
    BFC则是表示盒子从上到下的垂直排列方式,仅此而已(注意:一个盒子仅且仅有一个FC值)。而block-level box的FC特性值固定为BFC。
    collapsing margins规则
    1. 元素自身margin-top/bottom collapsing

    XHTML

    anonymous block-level box <div class="margins"></div> anonymous block-level box <div class="margins border"></div> anonymous block-level box

    1
    2
    3
    4
    5
    anonymous block-level box
    <div class="margins"></div>
    anonymous block-level box
    <div class="margins border"></div>
    anonymous block-level box

    CSS

    .margins{margin: 50px 0 70px;} .border{border: solid 1px red;}

    1
    2
    .margins{margin: 50px 0 70px;}
    .border{border: solid 1px red;}

    新葡亰496net 65
    当block-level box高度为0,垂直方向的border和padding为0,并且没有in-flow的子元素。那么它垂直方向的margin将会发生重叠。

    2. 父子元素margin-top/top 或 margin-bottom/bottom collapsing

    XHTML

    anonymous block-level box <div class="parent-margins"> <div class="margins border"></div> anonymous block-level box <div class="margins border"></div> </div> anonymous block-level box <div class="parent-margins border"> <div class="margins border"></div> anonymous block-level box <div class="margins border"></div> </div> anonymous block-level box

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    anonymous block-level box
    <div class="parent-margins">
      <div class="margins border"></div>
      anonymous block-level box
      <div class="margins border"></div>
    </div>
    anonymous block-level box
    <div class="parent-margins border">
      <div class="margins border"></div>
      anonymous block-level box
      <div class="margins border"></div>
    </div>
    anonymous block-level box

    CSS

    .parent-margins{margin: 25px 0;} .margins{margin: 50px 0 25px;} .border{border: solid 1px red;}

    1
    2
    3
    .parent-margins{margin: 25px 0;}
    .margins{margin: 50px 0 25px;}
    .border{border: solid 1px red;}

    新葡亰496net 66
    当父子元素margin-top间或margin-bottom间没有padding、border阻隔时,则会margin会发生重叠。
    注意空白字符会造成目标父子元素间的存在anonymous block-level box,导致margin不重叠。

    XHTML

    anonymous block-level box <div class="parent-margins">  <div class="margins border"></div> anonymous block-level box <div class="margins border"></div> </div> anonymous block-level box

    1
    2
    3
    4
    5
    6
    7
    anonymous block-level box
    <div class="parent-margins">&nbsp;
      <div class="margins border"></div>
      anonymous block-level box
      <div class="margins border"></div>
    </div>
    anonymous block-level box

    新葡亰496net 67

    3. 兄弟元素margin-bottom/top collapsing

    XHTML

    <div class="margins">former</div> <div class="margins">latter</div>

    1
    2
    <div class="margins">former</div>
    <div class="margins">latter</div>

    CSS

    .margins{margin: 50px 0 25px;}

    1
    .margins{margin: 50px 0 25px;}

    两个相邻的in-flow block-level box的上下margin将发生重叠。

    *上述为默认情况下block-level box(即display:block,其它为默认值时)的margin重叠规则***
    那非默认情况下呢?相比非默认情况下的margin重叠规则,我们更关心是什么时候不会产生重叠。这时又引入了另一个概念——生成新BFC。也就是block-level box A与block-level box B的FC特性值BFC可能是不同的。
    当两个相邻box的FC值不为同一个BFC时,它们的margin绝对不会重叠。
    那么剩下的问题就是,到底何时会产生新的BFC?哪些block-level box会采用新的BFC?默认BFC又是谁生成的呢?
    其实根元素(html)会生成默认BFC供其子孙block-level box使用。
    采用floats或absolute positioning作为positioning scheme时,或display:inline-block/table-cell/table-caption/flex/inline-flex或overflow属性值不为visible时,则会产生新的BFC;而新的BFC将作为子孙block-level box的FC属性值。
    注意:
        1. 产生新BFC的盒子不会与子盒子发生margin重叠;
        2. display:inline-block的盒子不与 兄弟 和 父 盒子发生margin重叠,是因为display:inline-block的盒子的FC特性值为IFC,还记得line box吗?没有margin重叠是自然不过的事了;
        3. positioning scheme为floats的盒子不与floated的兄弟盒子发生margin重叠,也不会与前一个in-flow的兄弟盒子发生margin重叠。(注意:与父盒子也不会发生margin重叠)

    XHTML

    <div class="margins border">sibling</div> <div class="margins border float">floats1</div> <div class="margins border float">floats2</div>

    1
    2
    3
    <div class="margins border">sibling</div>
    <div class="margins border float">floats1</div>
    <div class="margins border float">floats2</div>

    CSS

    .margins{margin: 50px 0 50px;} .border{border: solid 1px red;} .float{float:left;width:200px;}

    1
    2
    3
    .margins{margin: 50px 0 50px;}
    .border{border: solid 1px red;}
    .float{float:left;width:200px;}

    新葡亰496net 68

    归纳FC、BFC和IFC                      

    由于上述主要阐述inline/block-level box,因此通过“仅此而已”来简化BFC和IFC的内涵。下面我们稍微全面一点去理解BFC和IFC如何影响inline/block-level box。

    FC(Formatting Context),用于初始化时设置盒子自身尺寸和排版规则。注意“初始化”,暗指positioning scheme采用的是normal flow,要知道floats和absolute positioning均不是默认/初始化值。也就是说我们在讨论FC及BFC和IFC时,均针对in-flow box而言的。
      BFC
    **对于不产生新BFC的盒子**

    1. block-level boxes垂直排列,盒子的left outer edge与所在的containing block的左边相接触,默认情况下(width为auto时)right outer edge则与所在的containing block的右边相接触。即使存在floated的兄弟盒子。

    XHTML

    <div id="container" style="border:solid 2px red;"> <div id="left" style="float:left;width:300px;height:30px;background:yellow;opacity:0.2;"></div> <div id="right" style="height:30px;background:#999;"></div> </div>

    1
    2
    3
    4
    <div id="container" style="border:solid 2px red;">
      <div id="left" style="float:left;width:300px;height:30px;background:yellow;opacity:0.2;"></div>
      <div id="right" style="height:30px;background:#999;"></div>
    </div>

    新葡亰496net 69

    虽然 div#left 浮点了,但 div#right 的left outer edge还是与 div#container 的left content edge相接触。 div#right 所在的containing block就是 div#container 的content box.

    1. block-level box高度的计算
      The element’s height is the distance from its top content edge to the first applicable of the following:
      the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
      the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child’s bottom margin does not collapse with the element’s bottom margin
      the bottom border edge of the last in-flow child whose top margin doesn’t collapse with the element’s bottom margin
      zero, otherwise
      Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, and relatively positioned boxes are considered without their offset).

    也就out-flow box不影响block-level box高度的计算。也就是解释了为何div中仅含floated元素时,div盒子高度为0的现象了。

    **对于产生新BFC的盒子**
    对于产生新BFC的盒子而言,除了不发生collapsing margins的情况外,还有两个与浮点相关的现象。

    1. out-flow box纳入block-level box高度的计算
      In addition, if the element has any floating descendants whose bottom margin edge is below the element’s bottom content edge, then the height is increased to include those edges. Only floats that participate in this block formatting context are taken into account, e.g., floats inside absolutely positioned descendants or other floats are not.
      也就positioning scheme为floats的box也会影响block-level box高度的计算。

    2. 誓死不与positioning scheme为floats的兄弟盒子重叠
      The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with ‘overflow’ other than ‘visible’) must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space. They may even make the border box of said element narrower than defined by section 10.3.3. CSS2 does not define when a UA may put said element next to the float or by how much said element may become narrower.

    产生新BFC的block-level box不与floated-box重叠,而是floated-box的margin-box与block-level box的border-box相接触。
    水平方向

    XHTML

    <div style="float:left;width:100px;border: solid 1px red;margin-right:50px;">floated</div> <div style="width:200px;border: solid 1px blue;margin-left:100px;overflow:hidden;">gen new BFC balabala</div>

    1
    2
    <div style="float:left;width:100px;border: solid 1px red;margin-right:50px;">floated</div>
    <div style="width:200px;border: solid 1px blue;margin-left:100px;overflow:hidden;">gen new BFC balabala</div>

    新葡亰496net 70
    垂直方向

    XHTML

    <div style="float:left;width:100px;border: solid 1px red;margin-bottom:50px;">floated</div> <div style="width:200px;border: solid 1px blue;margin-top:100px;overflow:hidden;">gen new BFC balabala</div>

    1
    2
    <div style="float:left;width:100px;border: solid 1px red;margin-bottom:50px;">floated</div>
    <div style="width:200px;border: solid 1px blue;margin-top:100px;overflow:hidden;">gen new BFC balabala</div>

    新葡亰496net 71

     

     IFC

    提起IFC那就不能不说line box,而line box高度的计算方式上面已经叙述了,那line box的宽度呢?
    line box默认情况下左边框与containing block的左边框接触,右边框与containing block的右边框接触。若存在floated兄弟盒子,则line box的宽度为containing block的宽度减去floated-box的outer-box的宽度。
    新葡亰496net 72
    而inline-level box必须包含在line box中,若inline-level box的white-space:nowrap或pre外的其他值时,就会将inline-level box拆分为多个inline-level box并散落到多个line box中,从而实现文字环绕图片的效果了。
    新葡亰496net 73
    否则inline-level box会捅破line box(即line box宽度不变)

        行——换与不    

    先看看关于换行的CSS属性吧!

    white-space normal: 忽略/合并空白 pre: 保留空白,如同<pre>的行为 nowrap: 忽略/合并空白,文本不会换行,直到遇到<br/> pre-wrap: 保留空白,但是会正常地进行换行 pre-line: 忽略/合并空白,但是会正常地进行换行 inherit: 从父元素继承。 word-wrap normal: 只在允许的断字点换行 break-word: 在长单词或URL地址内部进行换行 word-break normal:依照亚洲和非亚洲语言的文本规则,允许在单词内换行。 keep-all:让亚洲语言文本如同非亚洲语言文本那样不允许在任意单词内换行。 break-all:允许非亚洲语言文本行如同亚洲语言文本那样可以在任意单词内换行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    white-space
        normal: 忽略/合并空白
        pre: 保留空白,如同<pre>的行为
        nowrap: 忽略/合并空白,文本不会换行,直到遇到<br/>
        pre-wrap: 保留空白,但是会正常地进行换行
         pre-line: 忽略/合并空白,但是会正常地进行换行
        inherit: 从父元素继承。
      word-wrap
        normal: 只在允许的断字点换行
        break-word: 在长单词或URL地址内部进行换行
      word-break
        normal:依照亚洲和非亚洲语言的文本规则,允许在单词内换行。
        keep-all:让亚洲语言文本如同非亚洲语言文本那样不允许在任意单词内换行。
        break-all:允许非亚洲语言文本行如同亚洲语言文本那样可以在任意单词内换行。

    具体示例可参考:css中强制换行word-break、word-wrap、white-space区别实例说明

    在处理换行问题上,我们要处理的对象分为亚洲语言文本和非亚洲语言文本。对于亚洲语言文本是以字作为操作单元,而非亚洲语言文本是以单词作为操作单元。而换行是针对特定语言文本的操作单元来处理,所以默认情况下会看到一串没空格的“中文”自动换行,而一串没空格的“英文”却没有换行的现象。
    对于我们(亚洲人)而言,一般采用 word-break:break-all;word-wrap:break-word; 来实现中英文自动换行效果,但英文单词本身是不能这样简单粗暴地换行的。
    英语单词移行有一定规则,归纳如下:
    1.移行处要用连字符号“-”,只占一个印刷符号的位置并放在该行的最后.
    2.移行时一般按照音节进行,故只可在两音节之间分开,不能把一个完整的音节分写在上下两行.例如:Octo-ber(正),Octob-er(误).
    3.复合词要在构成该词的两部分之间移行.如:some-thing,bed-room等.
    4.如果复合词原来就有连字符号,则就在原连字符号处分行.如:good-looking等.
    5.两个不同的辅音字母在一起时,移行时前后各一个.如:cap-tain,ex-pose等.
    6.当两个音节间只有一个辅音字母时,如果该辅音字母前的元音字母按重读开音节的规则发音,该辅音字母移至下一行.如:fa-ther等.但如果元音按重读闭音节的规则发音,则该辅音字母保留在上一行末尾.例如:man-age等.
    7.当遇到双写辅音字母时,一般把它们分成前后各一个.例如:mat-ter等.
    8.当重读音节在后面时,元音字母前的辅音字母通常移到下一行.如:po-lite等.
    9.单音节词不可移行.如:length,long,dance等.
    10.前缀或后缀要保持完整,不可分开写.如:unfit,disappear等.
    11.阿拉伯数字不分开移行书写.
    12.不论音节多少,专有名词不宜分写.例如:Nancy,Russia等.
    13.缩写词、略写词或某些词的缩写形式不可移行书写.例如:U.N.(联合国),P.R.C.(中华人民共和国),isn’t.
    14.不能构成一个音节的词尾不分写.例如:stopped等.
    15.字母组合或辅音连缀不可移行.例如:machine,meat等.

    CSS简化了上述的规则,若需要换行处恰好是一个复合词,就在原连字符号处分行;其它情况则整个单词移到下一行。因此使用 word-wrap:break-word; 就OK了。

    另外我们还可以通过 word-break:keep-all;white-space:nowrap; 来实现打死都不换行的效果
    总结                              
    洋洋洒洒总算把Box Model、BFC和IFC描述了个大概。对于BFC折腾点就是在collapsing margins那,另外还有产生新BFC这个行为上(这个跟浮动等有交集,以后再理清吧);而IFC重点在于理解line box,其实line box也像block-level box那样是垂直排列的,而inline-level box则是以line box作为容器实现水平排列罢了。到这里会发现理解IFC比BFC蛋疼多了,不过有了这篇作基础,后面理解text-align、line-height和vertical-align就轻松不少了。

    本文纯个人理解,若有纰漏,望各位指正,谢谢!

    感谢                              

    )

    )

    (IFC)

    [KB010: 常规流( Normal flow ) ]()
    [CSS 101: Block Formatting Contexts]()

    打赏支持我写出更多好文章,谢谢!

    打赏作者

    学习BFC

    2015/08/21 · CSS · BFC

    原文出处: elcarim的博客   

    BFC全称是Block Formatting Context,即块格式化上下文。它是CSS2.1规范定义的,关于CSS渲染定位的一个概念。要明白BFC到底是什么,首先来看看什么是视觉格式化模型。

    其实在文档解析过程中每个元素都会被描述为一个盒模型,然后一个盒子套进另外一个盒子,又会按照某种神秘的规则摆放,最后才形成了井井有条的页面。

    打赏支持我写出更多好文章,谢谢!

    任选一种支付方式

    新葡亰496net 74 新葡亰496net 75

    1 赞 3 收藏 评论

    视觉格式化模型

    视觉格式化模型(visual formatting model)是用来处理文档并将它显示在视觉媒体上的机制,它也是CSS中的一个概念。

    视觉格式化模型定义了盒(Box)的生成,盒主要包括了块盒、行内盒、匿名盒(没有名字不能被选择器选中的盒)以及一些实验性的盒(未来可能添加到规范中)。盒的类型由display属性决定。

    简单盒模型

    通常情况下,被更多人认知的盒模型就是简单盒模型,简单盒模型在早期IE时期有一种怪异模式下的解析方式,也就是现在的border-box,后来W3C制定的标准以后规定在标准解析模式下使用标准盒模型(content-box)。

    然后在CSS3推出的时候,增加了一个属性,也就是box-sizing,然后在查资料的时候在知乎看到一个段子,分享一下,哈哈。

    IE:我觉得盒模型应该是这样的,blahblah。

    W3C:明显应该是这样的才对,blahblah。

    结果是 IE 在怪异模式下用了「不标准」的盒模型,标准模式下用了「标准」的盒模型。
    围观群众:听说 IE 的盒模型不标准。

    ……多年过去……

    W3C:感觉还是 IE 的那个模型比较好。但我们已经回不去了……算了加个属性支持一下 IE 那种模式吧。

    box-sizing 这货就是用来擦屁股的,我从来没见过有人用 padding-box 的……

    作者:顾轶灵

    链接:https://www.zhihu.com/question/25509268/answer/30949718

    来源:知乎

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    所以在当前W3C标准中盒模型是可以通过box-sizing自由的进行切换的。然后到底这两种模式有什么区别呢,先看一张从chrome开发者工具中截的图。

    新葡亰496net 76

    • content-box(标准盒模型)

      • width = 内容的宽度

      • height = 内容的高度

    • border-box(怪异盒模型)

      • width = border padding 内容的宽度

      • height = border padding 内容的高度

    关于作者:^-^肥仔John

    新葡亰496net 77

    偏前端的临栈工程师 个人主页 · 我的文章 · 5 ·    

    新葡亰496net 78

    块盒(block box)

    块盒有以下特性:

    • 当元素的CSS属性displayblocklist-item或 table时,它是块级元素 block-level;
    • 视觉上呈现为块,竖直排列;
    • 块级盒参与(块格式化上下文);
    • 每个块级元素至少生成一个块级盒,称为主要块级盒(principal block-level box)。一些元素,比如<li>,生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒。

    视觉格式化模型(visual formatting model)

    CSS 视觉格式化模型(visual formatting model)是用来处理文档并将它显示在视觉媒体上的机制。

    简单盒模型需要进一步加工才能成为真正可以进行格式化的盒子,处理过程主要取决于一个css属性:display

    行内盒(inline box)

    • 当元素的CSS属性display的计算值为inlineinline-blockinline-table时,称它为行内级元素;
    • 视觉上它将内容与其它行内级元素排列为多行;典型的如段落内容,有文本(可以有多种格式譬如着重),或图片,都是行内级元素;
    • 行内级元素生成行内级盒(inline-level boxes),参与行内格式化上下文(inline formatting context)。同时参与生成行内格式化上下文的行内级盒称为行内盒(inline boxes)。所有display:inline的非替换元素生成的盒是行内盒;
    • 不参与生成行内格式化上下文的行内级盒称为原子行内级盒(atomic inline-level boxes)。这些盒由可替换行内元素,或 display 值为 inline-block 或inline-table 的元素生成,不能拆分成多个盒;

    块级盒、快容器盒、块盒、匿名块盒

    display的值为block list-item table 时,这些盒子会被标记为块级元素,在竖直方向一个接一个的排列,同时参与块级格式化上下文,

    每一个块级元素都至少生成一个块级盒,也可能是一个块容器盒,块容器盒所描述的是它和它的子元素之间的表现方式,块级盒所描述的是它与兄弟元素的表现方式。

    一个块容器盒只包含其他块级盒,或生成一个行内格式化上下文来只包含行内盒。当然你也许见过一段代码中某一个块容器盒同时包含行内盒和块级盒的情况,但实质上在这种情况下产生了一种新的匿名块盒来解决这个问题。

    先来看一段代码:

      <div class="blockContainerBox" style="background:red;height:100px;">
        <div class="blockLevelBox" style="background:blue;height:20px;width:20px;">
    
        </div>
        <div class="inlineBlockBox" style="background:green;height:20px;width:20px;display:inline-block;">
    
        </div>
        <div class="inlineBlockBox" style="background:green;height:20px;width:20px;display:inline-block;">
    
        </div>
      </div>
    

    首先blockContainerBox是一个块级盒同时也是一个块容器盒,这种情况下我们会把它称为块盒。ok,根据刚才的说法它只能包含块级盒或者生产一个行内格式化上下文来包含行内盒,而此时的代码不仅有blockLevelBox也有inlineBlockBox。此时浏览器会生成一个匿名块盒来包裹两个inlineBlockBox来生成一个行内格式化上下文。

    匿名盒(anonymous box)

    匿名盒也有份匿名块盒与匿名行内盒,因为匿名盒没有名字,不能利用选择器来选择它们,所以它们的所有属性都为inherit或初始默认值;

    如下面例子,会创键匿名块盒来包含毗邻的行内级盒:

    XHTML

    <div> Some inline text <p>followed by a paragraph</p> followed by more inline text. </div>

    1
    2
    3
    4
    5
    <div>
        Some inline text
        <p>followed by a paragraph</p>
        followed by more inline text.
    </div>

    新葡亰496net 79

    行内级盒、行内盒、匿名行内盒

    display 的值为 inline inline-block inline-table 时,这些盒子将被标记为行内级元素,在水平方向一个接一个排列,如果宽度不够排列将生成多行。

    当行内级盒参与行内格式化上下文以后被称为行内盒。

    所有display:inline 的非替换元素生成的盒是行内盒。而不参与生成行内格式化上下文的行内级盒称为原子行内级盒(atomic inline-level boxes)。

    匿名行内盒盒匿名块盒的原理类似,都是浏览器自动生成的补充性盒,简单看一段代码理解一下匿名行内盒是如何产生的。

    <p>
       台湾是<strong>中国不可分割的领土</strong>
    </p>
    

    此时“台湾是”就生成了一个匿名行内盒,然后与strong元素一起处于行内格式化上下文以后的p元素下,水平排列。

    三个定位方案

    在定位的时候,浏览器就会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。定位时,有三种定位方案,分别是常规流,浮动已经绝对定位。

    文档流

    这部分内容在#6已经讲过,在此不再赘述。

    常规流(Normal flow)

    • 在常规流中,盒一个接着一个排列;
    • 块级格式化上下文里面, 它们竖着排列;
    • 行内格式化上下文里面, 它们横着排列;
    • positionstaticrelative,并且floatnone时会触发常规流;
    • 对于静态定位(static positioning),position: static盒的位置是常规流布局里的位置
    • 对于相对定位(relative positioning),position: relative,盒偏移位置由这些属性定义topbottomleftandright即使有偏移,仍然保留原有的位置,其它常规流不能占用这个位置。

    这些盒子如何摆放呢?

    生成了这么多的盒子,那么我们需要把它们按照一定的规则摆放好,然后才能把整齐的盒子渲染成页面给用户看。盒子在摆放过程中,会通过盒的类型生成格式化上下文。也就是Block Formatting Context(块级格式化上下文)和Inline Formatting Context(行内格式化上下文)。

    浮动(Floats)

    • 盒称为浮动盒(floating boxes);
    • 它位于当前行的开头或末尾;
    • 导致常规流环绕在它的周边,除非设置 clear 属性;

    Block Formatting Context

    创建了BFC的元素规定了内部的块级盒如何布局,并且使该盒子在页面上形成一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。

    下列情况将创建一个块格式化上下文:

    • 根元素或其它包含它的元素
    • 浮动 (元素的 float 不为 none)
    • 绝对定位元素 (元素的 position 为 absolute 或 fixed)
    • 行内块 inline-blocks (元素的 display: inline-block)
    • 表格单元格 (元素的 display: table-cell,HTML表格单元格默认属性)
    • 表格标题 (元素的 display: table-caption, HTML表格标题默认属性)
    • overflow 的值不为 visible的元素
    • 弹性盒子 flex boxes (元素的 display: flex 或 inline-flex)

    摘自 MDN

    链接:https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context

    在一个块级格式化上下文的元素内部盒子

    • 内部的Box会在垂直方向,从顶部开始一个接一个地放置。
    • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生叠加
    • 每个元素的margin 子元素盒子的的左边, 与包含块border 父元素盒的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
    • BFC的区域不会与float box叠加。
    • 计算BFC的高度时,浮动元素也参与计算。

    绝对定位(Absolute positioning)

    • 绝对定位方案,盒从常规流中被移除,不影响常规流的布局;
    • 它的定位相对于它的包含块,相关CSS属性:topbottomleftright
    • 如果元素的属性positionabsolutefixed,它是绝对定位元素;
    • 对于position: absolute,元素定位将相对于最近的一个relativefixedabsolute的父元素,如果没有则相对于body

    margin塌陷的解释

    所谓的塌陷其实是两个BFC的相邻盒或者父子盒相互作用时产生的。

    在形成BFC的两个盒子会取两个盒子相邻边的最大margin作为相邻边的共用maring。

    块格式化上下文

    到这里,已经对CSS的定位有一定的了解了,从上面的信息中也可以得知,块格式上下文是页面CSS 视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域

    元素浮动时BFC的应用

    浮动元素会从正常文档流中删除掉,这也是为什么其他正常元素会看不见浮动的原因,也是为什么有父级塌陷的原因(因为在正常父级看来,浮动元素不在正常流中父级就什么都没有了,所以高度为0)。

    清楚浮动一种方法是使用overflow:auto/hidden,使用后整体形成了bfc,相当于清除了浮动。

    简单看一段代码:

    <div style="width:100px;">
        <div style="background:red;
                     width:50px;
                     height:50px;
                     float:left;"></div>
        <div class="aroundBox">我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块我要环绕那个红色块</div>
    </div>
    

    <div style="width:100px;background:blue;color:white;"><div style="background:red;width:50px;height:50px;float:left;"></div><div class="aroundBox">我要环绕那个红色块我要环绕那个红色块</div></div>


    因为红色块浮动所以产生了文字环绕的样式,如果说我们让aroundBox产生一个BFC,会变成什么样子呢?现在我们给aroundBox增加overflow:hidden


    <div style="width:100px;background:blue;color:white;"><div style="background:red;width:50px;height:50px;float:left;"></div><div style="overflow:hidden;">我要环绕那个红色块我要环绕那个红色块</div></div>


    红色块因为浮动产生了一个BFC,aroundBox因为overflow:hidden产生了一个BFC,因为BFC的区域不会与float box叠加。所以有了这样的变化。

    BFC的创建方法

    • 根元素或其它包含它的元素;
    • 浮动 (元素的float不为none);
    • 绝对定位元素 (元素的positionabsolutefixed);
    • 行内块inline-blocks(元素的 display: inline-block);
    • 表格单元格(元素的display: table-cell,HTML表格单元格默认属性);
    • overflow的值不为visible的元素;
    • 弹性盒 flex boxes (元素的display: flexinline-flex);

    但其中,最常见的就是overflow:hiddenfloat:left/rightposition:absolute。也就是说,每次看到这些属性的时候,就代表了该元素以及创建了一个BFC了。

    Inline Formatting Context

    相对于块格式化上下文,在行内格式化上下文中,盒子一个接一个地水平排列,起点是包含块的顶部。

    水平方向上的 margin,border 和 padding 在盒子之间得到保留。

    盒子在垂直方向上可以以不同的方式对齐:它们的顶部或底部对齐,或根据其中文字的基线对齐。

    对于非替换元素,比如a,span等标签可以设置水平方向上的margin但是无法设置垂直方向的margin,至于border和padding,垂直方向可以设置,但是当border-top和padding-top到达页面顶部后就不在增加了。

    而对于替换元素比如input、img等标签,是可以正常使用margin、border、padding的。


    想及时获取更多信息请关注我的公众号和博客

    新葡亰496net 80

    爱城

    BFC的范围

    BFC的范围在MDN中是这样描述的。

    A block formatting context contains everything inside of the element creating it that is not also inside a descendant element that creates a new block formatting context.

    中文的意思一个BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素。

    这段看上去有点奇怪,我是这么理解的,加入有下面代码,class名为.BFC代表创建了新的块格式化:

    XHTML

    <div id='div_1' class='BFC'> <div id='div_2'> <div id='div_3'></div> <div id='div_4'></div> </div> <div id='div_5' class='BFC'> <div id='div_6'></div> <div id='div_7'></div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <div id='div_1' class='BFC'>
    <div id='div_2'>
    <div id='div_3'></div>
    <div id='div_4'></div>
    </div>
    <div id='div_5' class='BFC'>
    <div id='div_6'></div>
    <div id='div_7'></div>
    </div>
    </div>

    这段代码表示,#div_1创建了一个块格式上下文,这个上下文包括了#div_2#div_3#div_4#div_5。即#div_2中的子元素也属于#div_1所创建的BFC。但由于#div_5创建了新的BFC,所以#div_6#div_7就被排除在外层的BFC之外。

    我认为,这从另一方角度说明,一个元素不能同时存在于两个BFC中

    BFC的一个最重要的效果是,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。这是利用BFC清除浮动所利用的特性,关于清除浮动将在后面讲述。

    如果一个元素能够同时处于两个BFC中,那么就意味着这个元素能与两个BFC中的元素发生作用,就违反了BFC的隔离作用,所以这个假设就不成立了。

    BFC的效果

    就如刚才提到的,BFC的最显著的效果就是建立一个隔离的空间,断绝空间内外元素间相互的作用。然而,BFC还有更多的特性:

    Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.

    In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the ‘margin’ properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

    In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

    简单归纳一下:

    1. 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);
    2. 处于同一个BFC中的元素相互影响,可能会发生margin collapse;
    3. 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此;
    4. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然;
    5. 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算;
    6. 浮动盒区域不叠加到BFC上;

    这么多性质有点难以理解,但可以作如下推理来帮助理解:html的根元素就是<html>,而根元素会创建一个BFC,创建一个新的BFC时就相当于在这个元素内部创建一个新的<html>,子元素的定位就如同在一个新<html>页面中那样,而这个新旧html页面之间时不会相互影响的。

    上述这个理解并不是最准确的理解,甚至是将因果倒置了(因为html是根元素,因此才会有BFC的特性,而不是BFC有html的特性),但这样的推理可以帮助理解BFC这个概念。

    从实际代码来分析BFC

    讲了这么多,还是比较难理解,所以下面通过一些例子来加深对BFC的认识。

    实例一

    CSS

    <style> * { margin: 0; padding: 0; } .left{ background: #73DE80; /* 绿色 */ opacity: 0.5; border: 3px solid #F31264; width: 200px; height: 200px; float: left; } .right{ /* 粉色 */ background: #EF5BE2; opacity: 0.5; border: 3px solid #F31264; width:400px; min-height: 100px; } .box{ background:#888; height: 100%; margin-left: 50px; } </style> <div class='box'> <div class='left'> </div> <div class='right'> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <style>
    * {
    margin: 0;
    padding: 0;
    }
    .left{
    background: #73DE80; /* 绿色 */
    opacity: 0.5;
    border: 3px solid #F31264;
    width: 200px;
    height: 200px;
    float: left;
    }
    .right{ /* 粉色 */
    background: #EF5BE2;
    opacity: 0.5;
    border: 3px solid #F31264;
    width:400px;
    min-height: 100px;
    }
    .box{
    background:#888;
    height: 100%;
    margin-left: 50px;
    }
    </style>
    <div class='box'>
    <div class='left'> </div>
    <div class='right'> </div>
    </div>

    显示效果:

    新葡亰496net 81

    绿色框(’#left’)向左浮动,它创建了一个新BFC,但暂时不讨论它所创建的BFC。由于绿色框浮动了,它脱离了原本normal flow的位置,因此,粉色框(’#right’)就被定位到灰色父元素的左上角(特性3:元素左边与容器左边相接触),与浮动绿色框发生了重叠。

    同时,由于灰色框(’#box’)并没有创建BFC,因此在计算高度的时候,并没有考虑绿色框的区域(特性6:浮动区域不叠加到BFC区域上),发生了高度坍塌,这也是常见问题之一。

    实例二

    现在通过设置overflow:hidden来创建BFC,再看看效果如何。

    XHTML

    .BFC{ overflow: hidden; } <div class='box BFC'> <div class='left'> </div> <div class='right'> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    .BFC{
    overflow: hidden;
    }
     
    <div class='box BFC'>
    <div class='left'> </div>
    <div class='right'> </div>
    </div>

    新葡亰496net 82

    灰色框创建了一个新的BFC后,高度发生了变化,计算高度时它将绿色框区域也考虑进去了(特性5:计算BFC的高度时,浮动元素也参与计算);

    而绿色框和红色框的显示效果仍然没有任何变化。

    实例三

    现在,现将一些小块添加到粉色框中,看看效果:

    XHTML

    <style> .little{ background: #fff; width: 50px; height: 50px; margin: 10px; float: left; } </style> <div class='box BFC'> <div class='left'> </div> <div class='right'> <div class='little'></div> <div class='little'></div> <div class='little'></div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <style>
    .little{
    background: #fff;
    width: 50px;
    height: 50px;
    margin: 10px;
    float: left;
    }
    </style>
     
    <div class='box BFC'>
    <div class='left'> </div>
    <div class='right'>
    <div class='little'></div>
    <div class='little'></div>
    <div class='little'></div>
    </div>
    </div>

    新葡亰496net 83

    由于粉色框没有创建新的BFC,因此粉色框中白色块受到了绿色框的影响,被挤到了右边去了。先不管这个,看看白色块的margin。

    实例四

    利用同实例二中一样的方法,为粉色框创建BFC:

    XHTML

    <div class='box BFC'> <div class='left'> </div> <div class='right BFC'> <div class='little'></div> <div class='little'></div> <div class='little'></div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    <div class='box BFC'>
    <div class='left'> </div>
    <div class='right BFC'>
    <div class='little'></div>
    <div class='little'></div>
    <div class='little'></div>
    </div>
    </div>

    新葡亰496net 84

    一旦粉色框创建了新的BFC以后,粉色框就不与绿色浮动框发生重叠了,同时内部的白色块处于隔离的空间(特性4:BFC就是页面上的一个隔离的独立容器),白色块也不会受到绿色浮动框的挤压。

    总结

    以上就是BFC的分析,BFC的概念比较抽象,但通过实例分析应该能够更好地理解BFC。在实际中,利用BFC可以闭合浮动(实例二),防止与浮动元素重叠(实例四)。同时,由于BFC的隔离作用,可以利用BFC包含一个元素,防止这个元素与BFC外的元素发生margin collapse。

    参考

    视觉格式化模型 | MDN

    块格式化上下文| MDN

    CSS之BFC详解

    W3C block-formatting

    1 赞 5 收藏 评论

    新葡亰496net 85

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:如何营造和睦的文化系列,前端布

    关键词: