# 定位与浮动

# 一、正常布局流

正常布局流(正常文档布局流)是一种在不对页面进行任何布局控制时,浏览器默认使用的 HTML 布局方式。具体来说就是,元素会按照书写模式顺序还有元素各自的特性去排列。

比如在水平书写模式下,一般是从左到右从上到下的顺序,内联元素的特性是在一行里水平排列,块级元素是占满一行的垂直式排列。那么最终,内联元素水平从左到右排列,块级元素垂直从上到下排列。

<html>
  <head>
    <style type="text/css">
      div {
        background-color: red;
      }
      span {
        background-color: green;
      }
    </style>
  </head>
  <body>
    <!--div是块级元素它是垂直从上到下排列的-->
    <div>div元素1</div>
    <div>div元素2</div>
    <!--span是内联元素它是水平从左到右排列的-->
    <span>span元素1</span>
    <span>span元素2</span>
  </body>
  <html></html>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 二、定位

定位是为了精确地定义元素框将要出现的位置,可能会将元素从正常文档流中取出。定位使用position属性,其值是static | relative | absolute | fixed | sticky

# 2.1 static 静态定位

position: static是静态定位,将元素放入它在文档布局流中的正常位置,相当于元素就是默认处于正常文档布局流中。

<html>
  <head>
    <style type="text/css">
      .positioned {
        position: static; /* 这行写上没写上都一样 */
        background: yellow;
      }
    </style>
  </head>
  <body>
    <p class="positioned">...</p>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.2 relative 相对定位

position: relative是相对定位,元素会相对于元素原本的位置进行偏移。元素其实还在正常文档布局流中,只是相比于原来位置被推移出一段距离(可能会遮挡其他元素)。偏移使用topbottomleftright这四个属性,值一般是正值,如果是负值那就是反方向的。

<html>
  <head>
    <style type="text/css">
      .outer {
        width: 200px;
        height: 100px;
        border: 1px solid blue;
      }
      .inner {
        width: 100px;
        height: 50px;
        top: 50px; /*元素上方往下推移50px*/
        left: 100px; /*元素左边往右推移100px*/
        position: relative;
        background: yellow;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner"></div>
    </div>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 2.3 absolute 绝对定位

position: absolute是绝对定位,元素会脱离正常文档布局流,在独立的一层里显示,它也有偏移只不过相对于父级元素来说的。偏移使用topbottomleftright这四个属性,值一般是正值,如果是负值那就是反方向的。

绝对定位和相对定位的差别就是,一个脱离了正常文档流,而另一个还在正常文档流里,绝对定位元素脱离后它原本的位置会被文档流里的其他元素占据,而相对定位元素的则不会。

另外,绝对定位是有一个前提的,就是父级元素得开启定位,不然其实跟后面要讲的固定定位一样是相对于页面来定位的。比如父级元素开启相对定位,子级元素开启绝对定位,那此时子级元素是对于父级元素的来定位的,否则就会是相对于整个页面来定位。

<html>
  <head>
    <style type="text/css">
      .outer {
        position: relative; /*这个去掉后,子元素的绝对定位就会相对于页面了*/
        width: 100px;
        height: 400px;
        padding: 0 25px;
        margin: 50px;
        border: 1px solid blue;
      }
      .inner1,
      .inner2,
      .inner3 {
        width: 100px;
        height: 100px;
        margin: 25px 0;
        border: 1px solid red;
      }
      .inner1 {
        background: yellow;
      }
      .inner2 {
        position: absolute; /*脱离文档流,相对于父级定位,那么父级的内边距是影响不到子元素了*/
        /*偏移没设置那就是auto,也就是原本的位置,设置0就是父级边框左上角位置*/
        top: 40px; /*偏移后的效果也是得考虑元素的布局尺寸的,也就是外边距,40+25=65px*/
        left: 40px; /*左边距为0,从左往右推移40px*/
        background: green;
      }
      .inner3 {
        background: red;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner1"></div>
      <div class="inner2"></div>
      <div class="inner3"></div>
    </div>
  </body>
</html>
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

topbottomleftright对绝对定位元素间隔的影响:

  1. topbottomleftright这四个值默认值是auto,表示元素虽然脱离了正常文档流但还是在原位,其实原位也被别人占据了(重叠了)。
  2. 如果topbottom中有一个是auto而另一个是有效数值,那按照有效数值那个属性来偏移;leftright也是一样的情况。
  3. 如果左右只设置一个,上下也只设置一个的话,其实就是将前两条规则结合起来了。比如left:0;top:auto;那就是左边间距 0 而上下还是在原来位置(元素 Y 轴距离还是原来位置),left:auto;top:10px;同理,上边距 10px,左右还在原来位置(元素 X 轴距离还是原来位置)。
  4. 如果那四个属性都是设置的有效数值,一般按照正常文档流的左和上来决定lefttop优先。
  5. 那四个属性都是设置的有效数值,但是又来了个margin。如果 margin 在一个方向上是 auto,那绝对定位元素的这个方向上还是在原位。跟那四个设置 auto 情况类似(规则 1),其他情况也基本类似(宽高影响还有规则 234)。
  6. 如果那四个属性还有margin都是设置的有效数值,那间隔将会是累积而不是折叠,其实就是对布局尺寸进行偏移。比如left: 2em;margin-left: 2em;,左侧间隔就是 4em。

将这六条样式代码放到下面例子的 span 样式里,就可以看到上面 6 条不同规则显示效果:
top: auto; bottom: auto; left: auto; right: auto;// 原位
top: 10px; bottom: auto; left: 20px; right: auto;// top 和 left 生效
top: auto; left: 0;top: 10px; left: auto;// top 或 left 生效
top: 0; left: 2em; right: 1em; width: 5em; background: silver;// top 和 left 优先
top: 1em; left: 1em; right: 1em; width: 5em; margin: 0 auto; background: silver;// margin 中的 auto 会优先,而 margin 中的 0 不会优先
top: 0; left: 2em; right: 12em; width: 5em; margin-left: 2em; margin-right: 1em; background: silver;// 间隔累积 4em,其实就是对布局尺寸进行偏移

<html>
  <head>
    <style type="text/css">
      p {
        position: relative;
        padding-left: 20px;
        width: 500px;
      }
      span {
        position: absolute;
      }
    </style>
  </head>
  <body>
    <p>
      When we consider the effect of positioning, it quickly becomes clear that authors can do a great deal of damage to
      layout, just as they can do very interesting things.<span>[4]</span>
      This is usually the case with useful technologies: the sword always has at least two edges, both of them sharp.
    </p>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2.4 fixed 固定定位

绝对定位会脱离正常文档流,然后相对于最近的祖先进行定位。而 fixed固定定位与之类似也会脱离正常文档流,但是相对于浏览器视窗 viewport 进行定位。

<html>
  <head>
    <style>
      body {
        width: 500px;
        height: 800px;
        margin: 0 auto;
      }
      h1 {
        position: fixed; /*固定定位*/
        top: 0px;
        width: 500px;
        margin-top: 0;
        background: white;
        padding: 10px;
      }
      p {
        background: aqua;
        border: 3px solid blue;
        padding: 10px;
        margin: 10px;
      }
      p:nth-of-type(1) {
        margin-top: 62px; /* 最顶上的p需要与顶部有间隔,防止一开始被h1遮挡 */
      }
    </style>
  </head>
  <body>
    <h1>Fixed positioning</h1>
    <p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p>
    <p>
      Now I'm absolutely positioned relative to the <code>&lt;body&gt;</code> element, not the
      <code>&lt;html&gt;</code> element!
    </p>
    <p>
      We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our
      margins, not both.
    </p>
  </body>
</html>
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

# 2.5 sticky 粘性定位

粘性定位:当元素滚动到父容器边缘时会黏在容器边缘,黏住的前提是在滚动方向上设置了合适的偏移。

偏移就是前面几节提到的topbottomleftright,偏移量的设置会让元素在黏住的地方形成一种相对定位,不过这个相对定位只会体现在这个滚动方向上,比如上下滚动那设置topbottom就会让元素在上下黏住,而leftright就会无效除非左右也能滚动。

<html>
  <head>
    <style type="text/css">
      .outer {
        width: 400px;
        height: 300px;
        overflow-y: auto;
        margin: 50px auto;
        border: 1px solid;
      }
      .outer div {
        width: 90%;
        height: 100px;
        margin: 5%;
      }
      .inner1 {
        background: red;
      }
      .inner2 {
        position: sticky;
        top: 10px; /*内容上下滚动的,设置的是top,那就会在距离div上边缘10px的地方黏住*/
        left: 10px; /*内容上下滚动的,这个left是无效的*/
        background: skyblue;
      }
      .inner3 {
        background: blue;
      }
      .inner4 {
        background: green;
      }
      .inner5 {
        background: black;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner1"></div>
      <div class="inner2"></div>
      <div class="inner3"></div>
      <div class="inner4"></div>
      <div class="inner5"></div>
    </div>
  </body>
</html>
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

# 2.6 定位元素的隐藏等式

定位元素在包含块中有个隐藏的等式,它与块级元素在水平方向上的等式很像,只是多出了四个位移。

定位元素的水平方向: left + margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right + right = the width of the containing block

定位元素的垂直方向: top + margin-top + border-top-height + padding-top + height + padding-bottom + border-bottom-height + margin-bottom + bottom = the height of the containing block

# 2.7 z-index 对定位的影响

使用定位来控制元素时,难免会碰到元素重叠的情况,那么决定哪个元素在上面哪个元素在下面就很重要,可以使用z-index属性来控制它们。z-index的值是整数,负数、0、正数都可以的,数值越大里开发者(观察者)越近。

<html>
  <head>
    <style type="text/css">
      p {
        position: absolute;
        border: 1px solid;
        background: #ddd;
        margin: 0;
      }
      #one {
        top: 1em;
        left: 1em;
        width: 40%;
        height: 10em;
        z-index: 3;
      }
      #two {
        top: 2em;
        left: 15%;
        width: 50%;
        height: 4.5em;
        z-index: 10;
      }
      #three {
        top: 10%;
        left: 30%;
        width: 30%;
        height: 10em;
        z-index: 8;
      }
      p[id] em {
        position: absolute;
        top: -1em;
        left: -1em;
        width: 10em;
        height: 5em;
      }
      #one em {
        z-index: 100;
        background: hsla(0, 50%, 70%, 0.9);
      }
      #two em {
        z-index: 10;
        background: hsla(120, 50%, 70%, 0.9);
      }
      #three em {
        z-index: -343;
        background: hsla(240, 50%, 70%, 0.9);
      }
    </style>
  </head>
  <body>
    <p id="one">文本1 <em>强调文本1.1</em></p>
    <p id="two">文本2 <em>强调文本2.1</em></p>
    <p id="three">文本3 <em>强调文本3.1</em></p>
  </body>
</html>
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

对于上面这个例子你可能会对三个<em>z-index设置感到疑惑,因为父元素设置了z-index后会建立自己的本地堆栈上下文(包含块),后代元素也是跟随着父元素的z-index而会忽略自身的z-index,可以明确的是子元素会比父元素更高但不会超过其他比父元素高的包含块。

# 三、浮动

# 3.1 什么是浮动

什么是浮动?一个元素脱离正常的文档布局流,并吸附到其父容器的某一边。原本在浮动元素后面的元素,现在会占据它的位置,并且内容会围绕着浮动元素来展示。

“占据它的位置”的理解:后面是块级元素那就会占据原本浮动元素的那一行,后面是内联元素就会水平挪动到原本浮动元素那个位置。这样的话,后面的元素可能会被浮动元素所遮挡

“内容会围绕着浮动元素来展示”的理解:虽然我会占据你原本的位置,但是我里面的文字图片等内容是要围绕着你浮动元素展示的,也就是说内容不会被浮动元素所遮住。(但不要认为是子元素不会被遮挡,是文字图片等展示内容不会被遮挡)

浮动使用float属性,其值是left | right | none。left 和 right 就是分别向左和右浮动,而 none 完全是为了屏蔽其他地方样式的影响。

<html>
  <head>
    <style type="text/css">
      .box {
        float: left;
        width: 150px;
        height: 150px;
        border: 2px solid blue;
        border-radius: 8px;
        margin-right: 20px;
      }
    </style>
  </head>
  <body>
    <h1>Simple float example</h1>
    <div class="box">Float</div>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam dolor, eu lacinia lorem placerat
      vulputate. Duis felis orci, pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at ultricies
      tellus laoreet sit amet. Sed auctor cursus massa at porta. Integer ligula ipsum, tristique sit amet orci vel,
      viverra egestas ligula. Curabitur vehicula tellus neque, ac ornare ex malesuada et. In vitae convallis lacus.
      Aliquam erat volutpat. Suspendisse ac imperdiet turpis. Aenean finibus sollicitudin eros pharetra congue. Duis
      ornare egestas augue ut luctus. Proin blandit quam nec lacus varius commodo et a urna. Ut id ornare felis, eget
      fermentum sapien.
    </p>
  </body>
</html>
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

# 3.2 浮动规则

浮动元素的包含块是最近的块级祖先元素。浮动元素本身会形成块级框,即使这个元素之前是内联元素。具体浮动规则如下:

  1. 浮动元素的左右外边界不能超出包含块的左右内边界。
  2. 如果前面元素左浮动,那么后面浮动元素的左边界必定处于前面元素右边界的右侧,除非后面浮动元素的顶边在前面浮动元素的下边;右浮动的情况类比。
  3. 左右两边浮动的元素之间也不能重叠,如果放不下会自动往下挪。
  4. 浮动元素的顶边不能比父元素的内顶边高,与第 1 条相似,但父元素不一定是块级元素。
  5. 浮动元素的顶边不能比前方任何一个浮动元素或块级元素的顶边高;上一条是父子关系,这条是兄弟关系,“弟弟”最多和“哥哥”平齐。
  6. 浮动元素的顶边不能比它原本未浮动前所在框体顶边还高。
  7. 满足其他条件后,浮动元素要尽可能上移。
  8. 满足其他条件后,左浮动元素尽可能左移,右浮动元素尽可能右移。

以上规则的前提是不使用边距来影响浮动的位置,因为使用边距后可能会在视觉上让浮动元素的边界超出父级元素的边界,也让浮动元素在视觉上重叠。

# 3.3 清除浮动

当元素浮动时,其他元素的内容会围绕着浮动元素来展示,但总会有特殊场景需要某个元素不再围绕着浮动元素展示。

清除浮动使用clear属性,有三个值:left停止任何活动的左浮动,right停止任何活动的右浮动,both停止任何活动的左右浮动。

清除浮动的理解:因为浮动元素后面的元素会占据浮动元素的一行(块级元素)或者水平位置(内联元素),那么清除浮动后,后面的元素会退回到下一行(块级元素)或者水平其他位置(内联元素)。

比如下面这个例子的页脚,不加clear和加了clear效果不同:

<html>
  <head>
    <style type="text/css">
      body {
        width: 50%;
        max-width: 500px;
        margin: 0 auto;
      }
      div:nth-of-type(1) {
        width: 36%;
        float: left;
      }
      div:nth-of-type(2) {
        width: 30%;
        float: left;
        margin-left: 4%;
      }
      div:nth-of-type(3) {
        width: 26%;
        float: right;
      }
      /*加了clear后,页脚会退回到下一行,也就是单独新一行里*/
      footer {
        clear: both;
      }
    </style>
  </head>
  <body>
    <h1>Float disaster</h1>
    <div>
      <h2>First column</h2>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam dolor, eu lacinia lorem placerat
        vulputate. Duis felis orci, pulvinar id metus ut, rutrum luctus orci.
      </p>
    </div>
    <div>
      <h2>Second column</h2>
      <p>
        Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada ultrices. Phasellus turpis est,
        posuere sit amet dapibus ut, facilisis sed est.
      </p>
    </div>
    <div>
      <h2>Third column</h2>
      <p>
        Nam consequat scelerisque mattis. Duis pulvinar dapibus magna, eget congue purus mollis sit amet. Sed euismod
        lacus sit amet ex tempus, a semper felis ultrices. Maecenas a efficitur metus.
      </p>
    </div>
    <footer>
      <p>
        ©2016 your imagination. This isn't really copyright, this is a mockery of the very concept. Use as you wish.
      </p>
    </footer>
  </body>
</html>
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

# 3.4 浮动的注意点

  • 浮动元素的内外边距和边框如果影响过大,可能会破坏布局(其实不是浮动本身的问题)。为了减少计算量,可以设置box-sizing: border-box,只算 width 和外边距。

  • 围绕着浮动元素展示的内容与浮动元素之间的间隔,是完全取决于浮动元素设置的外边距的。其实就是之前类似的解释,你浮动了,我现在占据了你的位置,你我之间是没有间距的(但可能会是遮挡关系),而我的文字图片等内容会围绕着你展示,就算内容也设置外边距也只在我里面有效。

  • 浮动元素之间的外边距是没有折叠的,比如你外边距 50px 我外边距 60px,那你我之间的间隔是 110px,不像之前的外边距是有折叠就是 60px(两个中取最大值)。

  • 实际上,浮动元素在父元素中所占的面积的有效高度为 0。比如在上一节的例子中,如果去掉<footer>这个标签,那 body 实际高度只有<h1>这个高度。解决办法就是给浮动元素后面的元素进行清除浮动。刚好上一节例子里页脚也是需要清除浮动的,但不是每个页面都有页脚,那就可以使用一个空的<div>标签并设置clear: both即可。

<html>
  <head>
    <style type="text/css">
      * {
        box-sizing: border-box;
      }
      body {
        width: 90%;
        max-width: 800px;
        margin: 0 auto;
      }
      footer {
        margin-top: 4%;
      }
      div:nth-of-type(1) {
        width: 30%;
        float: left;
      }
      div:nth-of-type(2) {
        width: 30%;
        float: left;
        margin-left: 5%;
      }
      div:nth-of-type(3) {
        width: 30%;
        float: right;
      }
      .clearfix {
        clear: both;
      }
      .column,
      footer {
        padding: 1%;
        border: 2px solid black;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <h1>Float disaster</h1>
    <div class="column">
      <h2>First column</h2>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam dolor, eu lacinia lorem placerat
        vulputate. Duis felis orci, pulvinar id metus ut, rutrum luctus orci.
      </p>
    </div>
    <div class="column">
      <h2>Second column</h2>
      <p>
        Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada ultrices. Phasellus turpis est,
        posuere sit amet dapibus ut, facilisis sed est.
      </p>
    </div>
    <div class="column">
      <h2>Third column</h2>
      <p>
        Nam consequat scelerisque mattis. Duis pulvinar dapibus magna, eget congue purus mollis sit amet. Sed euismod
        lacus sit amet ex tempus, a semper felis ultrices. Maecenas a efficitur metus. Nullam tempus pharetra pharetra.
      </p>
    </div>
    <!--空div用于清除浮动,可以解决footer围绕问题,也可以让前面的浮动div有实际面积-->
    <div class="clearfix"></div>
    <footer>
      <p>
        ©2016 your imagination. This isn't really copyright, this is a mockery of the very concept. Use as you wish.
      </p>
    </footer>
  </body>
</html>
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
62
63
64
65
66
67
68
69
70

# 3.5 边距对浮动的影响

建议先看边距对盒子的影响这一节。浮动是脱离了正常文档流,但也还是符合“边距对盒子的影响”的,只不过是在当前的布局流也就是浮动布局中的,并且也要符合浮动规则。

同向设置外边距:给浮动元素添加外边距,或者是给浮动元素添加外边距。
反向设置外边距:给浮动元素添加外边距,或者是给浮动元素添加外边距。

浮动元素的布局尺寸超过一行剩余空间就会跳到下一行里浮动,而小于等于上一行剩余空间就可能会跳到上一行里浮动(包含剩余空间为 0 的情况,此时布局尺寸也为 0,其实就是圣杯布局里使用的手段)。

四种情况:

  • 如果同向设置外边距,且外边距是正值。那么布局尺寸增大,展示尺寸不变,在前方增加的,会把展示效果往后推移。比如float: left;margin-left: 25px,布局尺寸宽度增大 25px,展示尺寸不变,但是展示效果会被从左往右(从前往后)推移 25px。
  • 如果同向设置外边距,且外边距是负值。那么布局尺寸减少,展示尺寸不变,在前方减小的,会把展示效果往前拉移。比如float: left;margin-left: -25px,布局尺寸宽度减少 25px,展示尺寸不变,但是展示效果会被从右往左(从后往前)拉移 25px。
  • 如果反向设置外边距,且外边距是正值。那么布局尺寸增大,展示尺寸不变,在后方增加的,也不会影响展示效果的位置。比如float: right;margin-left: 25px,只有布局尺寸宽度增大了 25px,其他没变。
  • 如果反向设置外边距,且外边距是负值。那么布局尺寸减少,展示尺寸不变,在后方减少的,也不会影响展示效果的位置。比如float: right;margin-left: -25px,只有布局尺寸宽度减少了 25px,其他没变。

可以看到下面这个例子,父级内边距 25px 与浮动元素外边距-25px 抵消了(布局尺寸宽度减小25px),显示效果就是浮动元素的左边界与父级元素的左边界重合(从左往右位移 25px)。

<html>
  <head>
    <style>
      .outer {
        width: 100px;
        height: 100px;
        padding: 25px;
        border: 1px solid red;
      }
      /*同向设置外边距,外边距是负值。布局尺寸宽度减少25px,展示尺寸不变,但是展示效果会被从右往左(从后往前)拉移25px*/
      .inner {
        width: 100px;
        height: 50px;
        float: left;
        background-color: red;
        margin-left: -25px;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner"></div>
    </div>
  </body>
</html>
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

为了方便,将“跳到上一行里浮动”简称为“上跳”,将“跳到下一行里浮动”简称为“下跳”。上跳有个前提,需要遵守“浮动元素的顶边不能比它原本未浮动前所在框体顶边还高”这条规则。还有不管上跳还是下跳,都只会跳一行,也就是说正负情况下,都只有各自的一个临界点来跳行。

<html>
  <head>
    <style>
      .outer {
        width: 100px;
        height: 100px;
        padding: 25px;
        border: 1px solid red;
      }
      .inner {
        width: 50px;
        height: 50px;
        float: left;
      }
      /* 当前这一行的的剩余空间100-50=50px */
      .col1 {
        background-color: red;
      }
      /* 同向设置外边距,外边距是正值。此时布局尺寸是30+21=51px,要大于上一行的的剩余空间50px,所以下跳了 */
      .col2 {
        width: 30px;
        background-color: blue;
        margin-left: 21px;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner col1"></div>
      <div class="inner col2"></div>
    </div>
  </body>
</html>
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

下面这个例子就是上跳的示例,可以将.col2 的 margin-left 从 0 慢慢变到-75px。同向设置外边距,外边距是负值,布局尺寸减少,并且展示效果会从后往前拉移。这个布局尺寸小于等于上一行剩余位置时会发生“上跳”,如果再继续增大外边距的绝对值,还会继续从后往前拉移。

<html>
  <head>
    <style>
      .outer {
        width: 100px;
        height: 100px;
        padding: 25px;
        border: 1px solid red;
      }
      .inner {
        width: 100px;
        height: 50px;
        float: left;
      }
      /*同向设置外边距,外边距是负值。此时布局尺寸为100-25=75px,这一行还剩25px的空间*/
      .col1 {
        background-color: red;
        margin-left: -25px;
      }
      /*可以将margin-left从0慢慢变到-75px,一变到-75px就“上跳”了*/
      /*同向设置外边距,外边距是负值。此时布局尺寸为100-75=25px,刚好可以放到上一行(剩余空间25px)*/
      .col2 {
        background-color: blue;
        margin-left: -75px;
        opacity: 0.8;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner col1"></div>
      <div class="inner col2"></div>
    </div>
  </body>
</html>
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

浮动元素反向设置外边距,因为是在后面增加或者减少布局尺寸的,所以并不会影响到当前浮动元素的展示效果的位置,但是会对后面紧挨着它的浮动元素(如果有的话)有影响(后面元素会位移)。可以看下面这个对比例子。

<html>
  <head>
    <style>
      .outer {
        width: 100px;
        height: 100px;
        padding: 25px;
        border: 1px solid red;
      }
      .inner {
        width: 50px;
        height: 50px;
        box-sizing: border-box;
      }
      .col1 {
        float: left;
        background-color: red;
      }
      .col2 {
        float: left;
        background-color: blue;
      }
      .col3 {
        float: right;
        margin-right: -50px;
        border: 3px solid green;
      } /*同向设置外边距,有位移,在outer边界的右边显示*/
      /*.col3 { float: right; margin-left: -50px; border: 3px solid green; }*/ /*反向设置外边距,无位移,在outer边界的左边显示*/
      /*.col3 { float: left; margin-left: -50px; border: 3px solid green; }*/ /*正向设置外边距,有位移,在outer边界的左边显示*/
      /*.col3 { float: left; margin-right: -50px; border: 3px solid green; }*/ /*反向设置外边距,无位移,在outer边界的右边显示*/
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner col1"></div>
      <div class="inner col2"></div>
      <div class="inner col3"></div>
    </div>
  </body>
</html>
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

弄懂上面所有规则和例子,在后面三列布局中你会明白类似于margin-right:-100%的作用了,至于 100%就很简单了,是父级元素的 contentArea 宽度的百分比,不包含父级元素的内边距宽度哦!还有,在三列布局中不一定要使用margin-right:-100%,原因是父级的 contentArea 宽度比左右两列小时,margin-right:-100%就无法让左右两列上跳了。

# 四、BFC

# 4.1 什么是 BFC

格式化上下文(Formatting Context)是页面中的一个渲染区域,并且有自己的一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。

那么块级格式上下文(Block Formatting Context,BFC)就很好理解了,是一个块级的渲染区域,这个块级“容器”内部布局是独立的,并且也不会影响到外部元素(与外界隔离了)。

# 4.2 怎么触发 BFC

下列方式会触发 BFC:

  • 根元素:<html>
  • 溢出:除了overflow: visible
  • 浮动元素:除了float: none
  • 绝对定位元素:position 为absolute | fixed
  • 行内块元素、弹性元素、网格元素:display 为inline-block | flex | grid,弹性元素还包括display: inline-flex元素的直接子元素,网格元素还包括display: inline-grid元素的直接子元素;
  • 表格相关:display 为table | inline-table | table-row | table-row-group | table-header-group | table-footer-group | table-cell | table-caption
  • 多列相关:column-count 或 column-width 不为auto,column-span 为all的元素始终会创建一个新的 BFC,即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug);
  • 渲染相关:contain 为layout |content | paint的元素;
  • 其他:display: flow-root可以创建无副作用的 BFC。

# 4.3 BFC 的作用

可以规避块级盒子的上下外边距折叠问题,相当于两个容器挨着,但容器内部与容器壁之间有间距,两个间距加起来就是两个不同容器内部元素之间的距离。

<html>
  <head>
    <style type="text/css">
      /*display: flow-root会创建无副作用的BFC*/
      .container {
        display: flow-root;
      }
      p {
        width: 100px;
        height: 100px;
        background: lightblue;
        margin: 20px;
      }
    </style>
  </head>
  <body>
    <!--可以去掉两个div,看看p的之间的间距是否折叠-->
    <div class="container">
      <p></p>
    </div>
    <div class="container">
      <p></p>
    </div>
  </body>
  <html></html>
</html>
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

可以包裹住浮动元素,因为计算 BFC 的高度时,浮动元素也参与计算。效果上看是“清除浮动”,让浮动元素当作类似于内联块级盒子一样。

<html>
  <head>
    <style type="text/css">
      /*创建无副作用的BFC,可以去掉“display: flow-root”看一下outer这个div是否能包裹住浮动元素*/
      .outer {
        display: flow-root;
        border: 1px solid #000;
      }
      .inner {
        float: left;
        width: 100px;
        height: 100px;
        background: #eee;
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="inner"></div>
    </div>
  </body>
  <html></html>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

可以避免被浮动元素覆盖,因为 BFC 的区域不会与 float box 重叠。在浮动那节说过,浮动后面的元素会占据浮动之前的位置,那么浮动元素是很可能遮住后面的元素,但是后面元素的文本图片等内容会围绕着浮动元素来展示。那么 BFC 是让后面的元素也避开浮动元素,不被遮挡。

<html>
  <head>
    <style type="text/css">
      .left {
        float: left;
        width: 100px;
        height: 100px;
        background-color: red;
      }
      /*display: flow-root会创建无副作用的BFC*/
      .right {
        display: flow-root;
        width: 300px;
        height: 200px;
        background-color: #cccccc;
      }
    </style>
  </head>
  <body>
    <div class="left"></div>
    <div class="right">
      实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本实例文本
    </div>
  </body>
  <html></html>
</html>
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