深入理解CSS定位—浮动模型

前面我们讲到了绝对定位,在这篇文章中,我们将讲到3种定位模型中的浮动模型。主要参考

注意:第二小节基本参考一丝丝凉的内容,看过这篇就可以略过我的那部分内容。

1. 浮动

浮动:浮动的框可以左右移动,直到它的外边缘碰到包含框或另一个浮动框的边缘。和绝对定位元素一样,使用浮动后,该元素也会脱离文档流,即浮动框不属于文档中的普通流当一个元素浮动之后,不会影响到块级框的布局而只会影响内联框(通常是文本)的排列,文档中的普通流就会表现得和浮动框不存在一样,当浮动框高度超出包含框的时候,也就会出现包含框不会自动伸高来闭合浮动元素(“高度塌陷”现象)。顾名思义,就是漂浮于普通流之上,像浮云一样,但是只能左右浮动。

正是因为浮动的这种特性,导致本属于普通流中的元素浮动之后,包含框内部由于不存在其他普通流元素了,也就表现出高度为0(高度塌陷)。在实际布局中,往往这并不是我们所希望的,所以需要闭合浮动元素,使其包含框表现出正常的高度。

1.1 浮动设计的初衷与特性

浮动设计的初衷是为了实现文本环绕效果。明白了浮动的设计初衷,我们就能明白浮动的特性表现了。浮动具有以下两个特性:

  • 包裹性
  • 破坏性

单纯只是说浮动的特性:包裹性与破坏性,很多小伙伴可能会很迷糊,不知所云。那么下面我们结合案例来说明以下,便于理解。

包裹性

什么是包裹性呢?这个需要结合栗子理解。下面是一个普普通通的div小青年,给它加上背景色,可以看到,独占一行(块级元素特性)。同样的div,给它加上float:left,可以看到,框收缩了,紧紧包裹住它的内容。

body {
    margin: 0;
    padding:0;
}

.container {
    width: 800px;
    float: left;
    margin: 0 auto;

}

.normal {
    background-color: salmon;
}

.flo {
    float: left;
    background-color: seagreen;
}
  <div class="container">
       <div class="normal">普普通通的div小青年</div>
       <div class="flo">浮动小青年</div>
 </div>

具有包裹性的其它小伙伴:

  • display:inline-block/table-cell/...
  • position:absolute/fixed/sticky
  • overflow:hidden/scroll

破坏性

正如前文所说,“使用浮动后,元素会脱离文档的普通流,它不会影响到块级框的布局而只会影响内联框(通常是文本)的排列,文档中的普通流就会表现得和浮动框不存在一样,当浮动框高度超出包含框的时候,也就会出现包含框不会自动伸高来闭合浮动元素(“高度塌陷”现象)。“

栗子:

        <div class="container">
            <div class="flo">浮动小青年</div>
        </div>

正常情况(不给类flo加浮动):

给div加上浮动后,容器“高度塌陷”。

具有破坏性的其它小伙伴:

  • display:none
  • position:absolute/fixed/sticky

2. 清除浮动(更准确,闭合浮动)

由于浮动的特性,所以运用它来布局时候我们通常需要清除浮动,更严谨的说,是闭合浮动:使浮动元素闭合,从而减少浮动的影响。

2.1 清除浮动的方法

清除浮动的各种方法:

(1)添加额外的标签

​ 通过在浮动元素末尾添加一个空的标签例如div元素附加clearLboth;css属性,其他标签br等亦可。

 <div class="wrap" id="float1">
	<h2>1)添加额外标签</h2>
	<div class="main left">.main{float:left;}</div>
	<div class="side left">.side{float:right;}</div>
	<div style="clear:both;"></div>
</div>
<div class="footer">.footer</div>

优点:通俗易懂,容易掌握

缺点:可以想象通过此方法,会添加多少无意义的空标签,有违结构与表现的分离,在后期维护中将是噩梦,这是坚决不能忍受的,建议不要使用。

(2)使用 br标签和其自身的 html属性

​ 这个方法有些小众,br 有 clear=“all | left | right | none” 属性

 <div class="wrap" id="float2">
	<h2>2)使用 br标签和其自身的 html属性</h2>
	<div class="main left">.main{float:left;}</div>
	<div class="side left">.side{float:right;}</div>
	<br clear="all" />
</div>
<div class="footer">.footer</div>

优点:比空标签方式语义稍强,代码量较少

缺点:同样有违结构与表现的分离,不推荐使用

(3) 父元素设置 overflow:hidden;

​ 通过设置父元素overflow值设置为hidden;在IE6中还需要触发 hasLayout ,例如 zoom:1;

<div class="wrap" id="float3" style="overflow:hidden; *zoom:1;">
	<h2>3)父元素设置 overflow </h2>
	<div class="main left">.main{float:left;}</div>
	<div class="side left">.side{float:right;}</div>
</div>
<div class="footer">.footer</div>

优点:不存在结构和语义化问题,代码量极少

缺点:内容增多时候容易造成不会自动换行导致内容被隐藏掉,无法显示需要溢出的元素;04年POPO就发现overflow:hidden会导致中键失效,所以还是不要使用了.

(4) 父元素设置 overflow:auto 属性

​ 同样IE6需要触发hasLayout,和3差不多

<div class="wrap" id="float3" style="overflow:auto; *zoom:1;">
	<h2>3)父元素设置 overflow </h2>
	<div class="main left">.main{float:left;}</div>
	<div class="side left">.side{float:right;}</div>
</div>
<div class="footer">.footer</div>

优点:不存在结构和语义化问题,代码量极少。

缺点:多个嵌套后,firefox某些情况会造成内容全选;IE中 mouseover 造成宽度改变时会出现最外层模块有滚动条等,firefox早期版本会无故产生focus等。

(5) 父元素也设置浮动

优点:不存在结构和语义化问题,代码量极少。

缺点:影响布局,不可能一直浮动到body,不推荐。

(6) 父元素设置display:table;

优点:结构语义化完全正确,代码量极少。

缺点:盒模型属性改变,会造成一系列问题,得不偿失,不推荐。

(6)使用:after 伪元素

​ 需要注意的是 :after是伪元素,不是伪类。由于IE6-7不支持:after,使用 zoom:1触发 hasLayout

/* 权衡方法 */
/* IE8+ */
.clearfix:after {
    content:"."; 
    display:block; 
    height:0; 
    visibility:hidden; /* 或者overflow:hidden; 推荐visbility*/
    clear:both; 
}
/*  IE6 /IE7 */
.clearfix { *zoom:1; }

优点:结构和语义化完全正确,代码量居中。

缺点:复用方式不当会造成代码量增加。

2.2 清除浮动总结

通过上面的方法对比,我们不难发现,其实以上列举的方法,无非有两类:

  • 浮动元素后插入一个具有clear:both的元素
    • div等
    • 或者伪元素:after
  • 通过设置使得父元素BFC(IE8+)或haslayout(IE6/ IE7)。

在讲述怎么触发BFC之前,我们应该了解以下什么是BFC,这点国内的博客介绍较少,Block formatting contexts(块级格式上下文)的简称。CSS3里面对这个规范做了改动,称之为:flow root,并且对触发条件进行了进一步说明。

2.2.1 如何触发BFC?

  • float除了none以外的值
  • overflow ( hidden, auto, scroll)
  • display(table-cell, table-caption, inline-block)
  • position (absolute, fixed)
  • fieldset元素

display:table ;本身并不会创建BFC,但是它会产生匿名框(anonymous boxes),而匿名框中的display:table-cell可以创建新的BFC,换句话说,触发块级格式化上下文的是匿名框,而不是display:table。所以通过display:table和display:table-cell创建的BFC效果是不一样的。

fieldset 元素在www.w3.org里目前没有任何有关这个触发行为的信息,直到HTML5标准里才出现。有些浏览器bugs(Webkit,Mozilla)提到过这个触发行为,但是没有任何官方声明。实际上,即使fieldset在大多数的浏览器上都能创建新的块级格式化上下文,开发者也不应该把这当做是理所当然的。CSS 2.1没有定义哪种属性适用于表单控件,也没有定义如何使用CSS来给它们添加样式。用户代理可能会给这些属性应用CSS属性,建议开发者们把这种支持当做实验性质的,更高版本的CSS可能会进一步规范这个。

2.2.2 BFC特性

  1. 块级格式化上下文会阻止外边距叠加

    当两个相邻的块框在同一个块级格式化上下文中时,它们之间垂直方向的外边距会发生叠加。换句话说,如果这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。

  2. 块级格式化上下文不会重叠浮动元素

    根据规定,一个块级格式化上下文的边框不能和它里面的元素的外边距重叠。这就意味着浏览器将会给块级格式化上下文创建隐式的外边距来阻止它和浮动元素的外边距叠加。由于这个原因,当给一个挨着浮动的块级格式化上下文添加负的外边距时将会不起作用(Webkit和IE6在这点上有一个问题)。

  3. 块级格式化上下文通常可以包含浮动

通俗地来说:创建了 BFC的元素就是一个独立的盒子,里面的子元素不会在布局上影响外面的元素,反之亦然,同时BFC仍然属于文档中的普通流。----by 一丝丝凉

至此,您或许明白了为什么 overflow:hidden或者auto可以闭合浮动了,答案是因为父元素创建了新的BFC。

对比BFC,IE6-7的显示引擎使用的是一个称为布局(layout)的内部概念,由于这个显示引擎自身存在很多的缺陷,直接导致了IE6-7的很多显示bug。当我们说一个元素“得到 layout”,或者说一个元素“拥有 layout” 的时候,我们的意思是指它的微软专有属性 hasLayout http://msdn.microsoft.com/worksh ... rties/haslayout.asp 为此被设为了 true 。IE6-7使用布局的概念来控制元素的尺寸和定位,那些拥有布局(have layout)的元素负责本身及其子元素的尺寸设置和定位。如果一个元素的 hasLayout 为false,那么它的尺寸和位置由最近拥有布局的祖先元素控制。

2.2.3 触发hasLayout的条件

在 IE7 中,overflow 也变成了一个 layout 触发器:

  • overflow: hidden|scroll|auto ( 这个属性在IE之前版本中没有触发 layout 的功能。 )

  • overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的属性,尚未得到浏览器的广泛支持。他们在之前IE版本中同样没有触发 layout 的功能)

IE8 使用了全新的渲染引擎,删除了 hasLayout 原本的功能,因此彻底杜绝了很多深恶痛绝的 bug,但 IE8~IE11 通过「document.documentElement.currentStyle.hasLayout」依然可以获得 hasLayout 的标志。

综上所述:

在支持BFC的浏览器(IE8+,firefox,chrome,safari)通过创建新的BFC闭合浮动;在不支持 BFC的浏览器 (IE6-7),通过触发 hasLayout 闭合浮动。

2.3 最佳实践

在上文各种清除浮动的方法中,无疑最好的方法是第7种,即:

/* 权衡方法 */
/* IE8+ */
.clearfix:after {
    content:"."; 
    display:block; 
    height:0; 
    visibility:hidden; /* 或者overflow:hidden; 推荐visbility*/
    clear:both; 
}
/*  IE6 /IE7 */
.clearfix { *zoom:1; }

下面详细说说该方法。

  1. display:block 使生成的元素以块级元素显示,占满剩余空间;

  2. height:0 避免生成内容破坏原有布局的高度。

  3. visibility:hidden 使生成的内容不可见,并允许可能被生成内容盖住的内容可以进行点击和交互;

4)通过 content:"."生成内容作为最后一个元素,至于content里面是点还是其他都是可以的,例如oocss里面就有经典的 content:"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",有些版本可能content 里面内容为空,博主一丝冰凉不推荐这样做的,firefox直到7.0 content:”" 仍然会产生额外的空隙;

5)zoom:1 触发IE hasLayout。

通过分析发现,除了clear:both用来闭合浮动的,其他代码无非都是为了隐藏掉content生成的内容,这也就是其他版本的闭合浮动为什么会有font-size:0; line-height:0;。

最佳实践

/*更好的方法,代码量更少*/
/* IE8+ */
.clearfix:before,
.clearfix:after {
	content:"";
	display:table;
	clear:both;
} 
/*  IE6 /IE7 */
.clearfix {	*zoom:1; }

由Nicolas Gallagher 大湿提出来的,原文:A new micro clearfix hack,该方法不存在firefox中空隙的问题。

需要注意的是:

上面的方法用到了 :before伪元素,很多人对这个有些迷惑,到底我什么时候需要用before呢?这里需要了解它的作用,即用来处理margin边距重叠的,由于内部元素 float 创建了BFC,导致内部元素的margin-top和 上一个盒子的margin-bottom 发生叠加。如果这不是你所希望的,那么就可以加上before,如果只是单纯的闭合浮动,after就够了!并不是如同大漠《Clear Float》一文所说的:但只使用clearfix:after时在跨浏览器兼容问题会存在一个垂直边距叠加的bug,这不是bug,是BFC应该有的特性。

需要注意,清除浮动不应该被滥用。通常只应该运用在浮动子元素的父级元素上。

3. 浮动与流体布局

合理的使用浮动,可以实现非常棒的流体布局。结合张鑫旭在慕课网的课程理解。

浮动与流体布局-张鑫旭

html结构:

<div class="mib_x mib_resize">
        <div class="mib_list">
            <a href="http://t.sina.com.cn/xuruoxuan" class="mib_head_a">
                <img id="mibHeadImg" title="徐若瑄VIVIAN" src="http://img.mukewang.com/53e2e9470001dfd200500050.jpg">
            </a>
            <div class="mib_cell">
                <p class="mib_sms"><a title="徐若瑄VIVIAN" href="#">徐若瑄VIVIAN<i title="新浪认证" class="mib_vip"></i></a>:一個人的晚餐!茶泡飯!飯、飯、飯… 今日不減肥,先把病治好再說! 我認真吃完這,燒就會退了吧?! 開動啦~~~~~~~~~~~~~~~~~~</p>
                <div class="feed_img"><img src="http://img.mukewang.com/53e2e9b10001948000890120.jpg" height="120"></div>
            </div>    
        </div>
</div>

1)浮动与两侧皆自适应的流体布局

body { 
    font-size: 14px; 
    background-color: #DDF3F7; 
    color: #333; 
}
a { 
    color: #0082CB; 
    text-decoration: none; 
}
p { margin: 0; } 
img { border: 0; }

.mib_body { 
    width: 600px; 
    margin-left: auto;
    margin-right: auto; 
}
.mib_x { 
    margin-bottom: 10px; 
    background-color: #fff; 
}
.mib_list { padding: 20px; }
.mib_resize { 
    overflow: auto; 
    resize: both; 
}
.mib_vip { 
    display: inline-block; 
    width: 11px; 
    height: 10px; 
    margin-left: 1px; 
    background: url(data:image/gif;base64,R0lGODlhCwAKAJEDAPyZCveDBuJmBP///yH5BAEAAAMALAAAAAALAAoAAAIdhD1zopgTXgMpsBdylVCPK2UCKI0j95hoRa0NVwAAOw==); 
}

.mib_head_a { 
    float: left; 
    margin-right: 20px; 
}

2)浮动与右侧尺寸固定的流体布局

<body>
<div id="mibBody" class="mib_body">
    <div class="mib_x mib_resize">
        <div class="mib_list">
            <a href="http://t.sina.com.cn/xuruoxuan" class="mib_head_r">
                <img title="徐若瑄VIVIAN" src="http://img.mukewang.com/53e2e9470001dfd200500050.jpg">
            </a>
            <div class="mib_feed_flow">
                <p class="mib_sms"><a title="徐若瑄VIVIAN" href="#">徐若瑄VIVIAN<i title="新浪认证" class="mib_vip"></i></a>:一個人的晚餐!茶泡飯!飯、飯、飯… 今日不減肥,先把病治好再說! 我認真吃完這,燒就會退了吧?! 開動啦~~~~~~~~~~~~~~~~~~</p>
                <div class="feed_img"><img src="http://img.mukewang.com/53e2e9b10001948000890120.jpg" height="120"></div>
            </div>
        </div>
    </div>
    <div class="mib_x mib_resize">
        <div class="mib_list">
        	<div class="mib_full_float">
                <div class="mib_feed_flow">
                    <p class="mib_sms"><a title="徐若瑄VIVIAN" href="#">徐若瑄VIVIAN<i title="新浪认证" class="mib_vip"></i></a>:一個人的晚餐!茶泡飯!飯、飯、飯… 今日不減肥,先把病治好再說! 我認真吃完這,燒就會退了吧?! 開動啦~~~~~~~~~~~~~~~~~~</p>
                    <div class="feed_img"><img src="http://img.mukewang.com/53e2e9b10001948000890120.jpg" height="120"></div>
                </div>
            </div>
            <a href="http://t.sina.com.cn/xuruoxuan" class="mib_head_l">
                <img title="徐若瑄VIVIAN" src="http://img.mukewang.com/53e2e9470001dfd200500050.jpg">
            </a>
        </div>
    </div>
</div>
</body>
ody { font-size: 14px; background-color: #DDF3F7; color: #333; }
a { color: #0082CB; text-decoration: none; }
p { margin: 0; } img { border: 0; }

.mib_body { width: 600px; margin-left: auto; margin-right: auto; }
.mib_x { margin-bottom: 10px; background-color: #fff; }
.mib_list { padding: 20px; overflow: hidden; _zoom: 1; resize: none; }
.mib_resize { overflow: auto; resize: both; }
.mib_vip { display: inline-block; width: 11px; height: 10px; margin-left: 1px; background: url(data:image/gif;base64,R0lGODlhCwAKAJEDAPyZCveDBuJmBP///yH5BAEAAAMALAAAAAALAAoAAAIdhD1zopgTXgMpsBdylVCPK2UCKI0j95hoRa0NVwAAOw==); }

/* 下面这个是右浮动,改变DOM位置的流体布局写法 */
.mib_head_r { width: 56px; float: right; }
.mib_feed_flow { margin-right: 76px; }
/* 下面这个是左浮动,不改变DOM位置的流体布局写法 */
.mib_full_float { width: 100%; float: left; }
.mib_head_l { width: 56px; float: left; margin-left: -56px;}

3)浮动与单侧尺寸固定的流体布局

<div id="mibBody" class="mib_body">
    <div class="mib_x">
        <div class="mib_list">
            <a href="http://t.sina.com.cn/xuruoxuan" class="mib_head_a">
                <img title="徐若瑄VIVIAN" src="http://img.mukewang.com/53e2e9470001dfd200500050.jpg">
            </a>
            <div class="mib_feed_fixed">
                <p class="mib_sms"><a title="徐若瑄VIVIAN" href="#">徐若瑄VIVIAN<i title="新浪认证" class="mib_vip"></i></a>:一個人的晚餐!茶泡飯!飯、飯、飯… 今日不減肥,先把病治好再說! 我認真吃完這,燒就會退了吧?! 開動啦~~~~~~~~~~~~~~~~~~</p>
                <div class="feed_img"><img src="http://img.mukewang.com/53e2e9b10001948000890120.jpg" height="120"></div>
            </div>
        </div>
    </div>
    <div class="mib_x">
        <div class="mib_list">
            <a href="http://t.sina.com.cn/xuruoxuan" class="mib_head_a">
                <img title="徐若瑄VIVIAN" src="http://img.mukewang.com/53e2e9470001dfd200500050.jpg">
            </a>
            <div class="mib_feed_flow">
                <p class="mib_sms"><a title="徐若瑄VIVIAN" href="#">徐若瑄VIVIAN<i title="新浪认证" class="mib_vip"></i></a>:一個人的晚餐!茶泡飯!飯、飯、飯… 今日不減肥,先把病治好再說! 我認真吃完這,燒就會退了吧?! 開動啦~~~~~~~~~~~~~~~~~~</p>
                <div class="feed_img"><img src="http://img.mukewang.com/53e2e9b10001948000890120.jpg" height="120"></div>
            </div>
        </div>
    </div>

</div>
body { font-size: 14px; background-color: #DDF3F7; color: #333; }
a { color: #0082CB; text-decoration: none; }
p { margin: 0; } img { border: 0; }

.mib_body { width: 600px; margin-left: auto; margin-right: auto; -webkit-transition: width .35s; transition: width .35s; }
.mib_x { margin-bottom: 10px; background-color: #fff; }
.mib_list { padding: 20px; overflow: hidden; _zoom: 1; }
.mib_vip { display: inline-block; width: 11px; height: 10px; margin-left: 1px; background: url(data:image/gif;base64,R0lGODlhCwAKAJEDAPyZCveDBuJmBP///yH5BAEAAAMALAAAAAALAAoAAAIdhD1zopgTXgMpsBdylVCPK2UCKI0j95hoRa0NVwAAOw==); }

.mib_head_a { width: 56px; float: left; }
/* 下面这个是固定布局写法 */
.mib_feed_fixed { width: 484px; float: right; }
/* 下面这个是流体布局写法 */
.mib_feed_flow { margin-left: 76px; }

4. 小结

本文从浮动设计初衷出发,讲述了浮动的特性,包括包裹性与破坏性;由于浮动给布局带来的影响,需要清理这种影响,故又给出了多种清理浮动的方法,并最终给出了最佳实践。同时,讲述清理浮动方法时,也给出了BFC与haslayout的部分知识,方便读者理解。希望读完这篇文章能给大家一些益处。

posted @ 2020-05-12 20:02  川南烟雨  阅读(553)  评论(0编辑  收藏  举报