# CSS3教程 - 9 定位2

继续讲解定位...


# 9.7 水平布局

我们之前在盒子模型的章节讲过,水平方向的布局存在如下等式:

margin-left + border-left + padding-left + width + padding-right + border-right + margin-right = 其父元素的宽度

当使用绝对定位时,需要添加 leftright 两个值(此时规则和之前一样,只是多添加了两个值)

left + margin-left + border-left + padding-left + width + padding-right + border-right + margin-right + right = 其包含块内容区的宽度

当发生过度约束时(就是等式不成立的时候):

  • 如果 9 个值中没有auto,则自动调整right值以使等式成立(之前 7 个值的时候调整的是margin-right
  • 如果 9 个值中有auto,则自动调整auto的值以使等式成立

可设置auto的值:margin-leftmargin-rightwidthleftright

因为leftright的值默认是auto,所以如果没有设置leftright,当等式不满足时,则会自动调整这两个值。


所以一个元素开启了绝对定位,又想让它在父元素中居中,则需要设置为如下:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      .box1 {
        width: 300px;
        height: 300px;
        background-color: lightskyblue;
        position: relative;  /* 父相对定位 */
      }

      .box2 {
        width: 100px;
        height: 100px;
        background-color: lightpink;
        position: absolute;  /* 子绝对定位 */
        margin-left: auto;
        margin-right: auto;
        left: 0px;
        right: 0px;
      }
    </style>
  </head>

  <body>
    <div class="box1">
      <div class="box2"></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
  • 首先父元素开启相对定位,子元素开启绝对定位;
  • 子元素设置 margin-leftmargin-rightautoleftright0px;这样左右的距离会被 margin-leftmargin-right 平分,从而让元素居中。

显示效果:

设置 margin-leftmargin-right0pxleftrightauto 可以让元素居中吗?

不可以,此时等式不成立,会自动调整 right 使等式成立,左移此时 box1 居左。

# 9.8 垂直布局

元素开启决定定位后,垂直方向布局的等式的也必须要满足:

top + margin-top + border-top + padding-top + height + padding-bottom + border-bottom + margin-bottom + top = 其包含块内容区的高度

通过这个等式,可以让元组在父元素中垂直居中。


举个栗子:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      .box1 {
        width: 300px;
        height: 300px;
        background-color: lightskyblue;
        position: relative;  /* 父相对定位 */
      }

      .box2 {
        width: 100px;
        height: 100px;
        background-color: lightpink;
        position: absolute;  /* 子绝对定位 */
        margin-top: auto;
        margin-bottom: auto;
        top: 0;
        bottom: 0;
      }
    </style>
  </head>

  <body>
    <div class="box1">
      <div class="box2"></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
  • 首先父元素开启相对定位,子元素开启绝对定位;
  • 子元素设置 margin-topmargin-bottomautotopbottom0px;这样上下的距离会被 margin-topmargin-bottom 平分,从而让元素居中。

显示如下:

# 9.9 水平垂直居中

有了上面水平和垂直居中的方式,那么就可以很容易实现子元素在父元素中水平和垂直居中了。

代码如下:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      .box1 {
        width: 300px;
        height: 300px;
        background-color: lightskyblue;
        position: relative;  /* 父相对定位 */
      }

      .box2 {
        width: 100px;
        height: 100px;
        background-color: lightpink;
        position: absolute;  /* 子绝对定位 */
        
        margin: auto;

        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
      }
    </style>
  </head>

  <body>
    <div class="box1">
      <div class="box2"></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
  • 首先父元素开启相对定位,子元素开启绝对定位;
  • 设置 margin 为 auto, 则 margin-leftmargin-rightmargin-topmargin-bottom 都为 auto,此时 left、right、top、bottom 为0,则距离在水平和垂直方向上被 margin 平分,从而达到水平和垂直方向上的居中。

显示如下:

# 9.10 元素层级

前面讲到,元素开启定位后,会调高元素显示的层级,也就会显示在别的元素上面。

如果元素都开启定位后,那谁在上面,谁在下面呢?

先看一个例子:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      .box1 {
        width: 100px;
        height: 100px;
        background-color: lightskyblue;
        position: absolute;  /* 绝对定位 */
      }

      .box2 {
        width: 100px;
        height: 100px;
        background-color: lightpink;
        position: absolute;  /* 绝对定位 */

        top: 50px;
        left: 50px;
      }

      .box3 {
        width: 100px;
        height: 100px;
        background-color: lightgreen;
        position: absolute;  /* 绝对定位 */

        top: 100px;
        left: 100px;
      }
    </style>
  </head>

  <body>
    <div class="box1">1</div>
    <div class="box2">2</div>
    <div class="box3">3</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

上面三个元素都开启了绝对定位,显示层级如何呢?

显示如下:

可以看到 box3 在最上面,box1在最下面。也就是说在没调整层级的时候,它们的层级是一样的,但是HTML 文档中后面的元素会覆盖前面的元素,因为 box3 在后面,所以 box3 显示在最上面。


元素开启定位后(非static),可以通过 z-index 来调整元素的层级。一个元素的位置其实是有三个维度 x/y/z ,Z轴是垂直于屏幕指向屏幕外的。

在上面代码的基础上,设置 box2 的层级:

.box2 {
  width: 100px;
  height: 100px;
  background-color: lightpink;
  position: absolute;  /* 绝对定位 */

  top: 50px;
  left: 50px;
  z-index: 1;  /* 设置层级 */
}
1
2
3
4
5
6
7
8
9
10

如果元素未指定 z-index,则默认是 z-index: auto,其顺序依赖文档结构,可以理解为 z-index0 。上面设置 box2 的 z-index 为 1,所以显示在 box1 和 box3 的上面。

显示如下:

可以通过z-index属性来指定元素的层级, z-index 的合理范围为 -999 到 9999,建议设置为正值。值越大元素的层级越高,元素的层级越高,越优先显示。层级一样,那么优先显示 HTML 文档结构中后面的元素;另外祖先的元素的层级再高,也不会盖住后代元素。

# 9.11 轮播图练习

下面来进行一个布局和定位联系,制作一个轮播图。

因为当前没有学习 JS,所以轮播图还不能动,只是简单的完成布局。

最终效果:

# 1 编写框架

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      
    </style>
  </head>

  <body>
    <ul class="image-wrapper">
      <li><a href="javascript:void(0);"><img src="./image/html5.webp" /></a></li>
      <li><a href="javascript:void(0);"><img src="./image/css3.webp" /></a></li>
      <li><a href="javascript:void(0);"><img src="./image/javascript.webp" /></a></li>

      <!-- 圆点 -->
        <div class="point-wrapper">
          <a href="javascript:void(0);"></a>
          <a href="javascript:void(0);"></a>
          <a href="javascript:void(0);"></a>
        </div>
    </ul>
  </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
  • 因为是轮播图,所以是多张图片,这里使用 <ul><li>,因为轮播图一般是可以点击的,所以使用 <a> 标签包裹。
  • 创建一个圆点列表,用于圆点的展示

现在没有样式,显示效果没眼看,继续...

# 2 编写CSS样式

  • 首先 <ul> 宽高根据图片的宽高来调整,我这里图片宽度是400,高度240,所以设置 <ul> 的宽高;
  • 多张图片是层叠在一起的,所以针对 <li> 开启绝对定位, <ul> 开启相对定位;
  • 这里让轮播图居中,所以设置 <ul>margin-leftmargin-rightauto

所以添加样式如下:

.image-wrapper {
  width: 400px;
  height: 240px;
  position: relative;

  margin-top: 20px;
  margin-left: auto;
  margin-right: auto;
}

.image-wrapper li {
  position: absolute;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

显示如下:

可以看到现在显示的是最后一张图,因为三个 <li> 的层级是一样的,我们可以通过调整 <li> 的层级来切换图片的显示,这里没有 JS,所以通过 CSS 来选中图片,调整层级:

.image-wrapper li:nth-child(2) {  /* 让第二张图片显示 */
  z-index: 1;
}
1
2
3

显示如下:

下面来调整圆点指示器:

  • 圆点指示器是在图片上面的,所以让包裹圆点的 div 进行绝对布局,并使用 left 和 bottom 控制其位置;
  • 各个圆点还没有设置大小,所以没法显示,但是 <a> 是行内元素,无法设置宽高,所以需要将其设置为块元素,这样可以调整宽高,但是设置为块元素多个 <a> 就会竖向排列,所以设置 <a> 向左浮动,横向排列;因为元素浮动后,行内元素会变成块元素,所以就不用通过 display:block 来设置 <a> 为块元素了。
  • 为了容易看见,我们先将 <a> 设置为红色背景;
  • 每个点有间隔,设置一下 margin;

所以添加样式如下:

.point-wrapper {
  position: absolute;  /* 设置为绝对定位 */
  bottom: 15px;
  left: 20px;
  z-index: 100;  /* 提高层级,显示在图片之上 */
}

.point-wrapper a {
  float: left;  /* 浮动 */
  width: 12px;
  height: 12px;
  margin-left: 5px;  /* 设置每个圆点间距 */
  background-color: red;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

显示如下:

下面继续设置圆点的样式:

  • 设置圆点为圆角 50%;
  • 设置圆点为白色,有透明度的;
  • 设置圆点鼠标悬浮后,添加白色边框;

所以继续添加样式:

.point-wrapper a {
  float: left;  /* 浮动 */
  width: 12px;
  height: 12px;
  margin-left: 5px;  /* 设置每个圆点间距 */
  border-radius: 50%;  /* 圆角50%,圆形 */
  background-color: rgba(255, 255, 255, 0.6);  /* 背景0.6透明度的白色 */
}

.point-wrapper a:hover {   /* 鼠标悬浮样式 */
  border: 2px solid rgba(255, 255, 255, 1);  /* 添加一个白色边框 */
}
1
2
3
4
5
6
7
8
9
10
11
12

显示如下:

出现了一个问题,鼠标悬浮后因为添加了边框,导致圆点元素尺寸变大,导致圆点大小不一样。

可以先这样处理:

  • 鼠标不悬浮的,也添加边框,透明的边框;
  • 然后让背景颜色不要蔓延到内边距和边框;(这个后面将背景的时候再讲)

添加如下样式

.point-wrapper a {
  float: left; 
  width: 12px;
  height: 12px;
  margin-left: 5px; 
  border-radius: 50%;  
  background-color: rgba(255, 255, 255, 0.6);  
  background-clip: content-box;  /* 将背景颜色设置到内容区,不蔓延到边框和内边距 */
  border: 2px solid transparent;  /* 添加透明边框 */
}
1
2
3
4
5
6
7
8
9
10

显示效果:

# 9.12 下拉菜单练习

我们经常看到将鼠标悬浮到导航栏上的菜单,可以显示一个下拉菜单,下面就来实现这样的功能。

最终效果如下:

在实现上面的效果之前,先介绍一下如何使用纯 CSS 实现上面的小三角。

先看一下下面的代码:

<!DOCTYPE html>
<html>
  <head>
    <style>
      .box1 {
        width: 50px;
        height: 50px;
        border: 20px solid red;
        border-color: red green blue yellow;
      }
    </style>
  </head>

  <body>
    <div class="box1"></div>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 代码很简单,就是给 div 设置了一个边框,每边的边框的颜色是不一样的。

显示如下:

可以看到各个边的边框是斜着的。如果将 div 的宽度和高度设置为 0 呢?

显示如下:

所以要想实现一个向上的小三角,现在只需要将上边框去掉,左右两个边框设置为透明即可(左右边框必须保留,不然没有小三角效果):

.box1 {
  width: 0px;
  height: 0px;
  border: 20px solid transparent;  /* 边框透明 */
  border-top: none;  /* 去掉上边框 */
  border-bottom-color: blue;  /* 设置下边框颜色 */
}
1
2
3
4
5
6
7

显示如下:

如果要实现向下、左右的三角也是同样的道理,当然你如果通过图片来实现也是可以的。


下面来实现下拉菜单。

首先先实现导航条:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      .nav {
        width: 1000px;
        height: 48px;
        background-color: #e8e7e3;
        margin: 50px auto; /* 左右margin auto,设置导航条居中,盒子模型章节讲过 */
      }

      /* li整体布局 */
      .nav li {
        float: left; /* 浮动li元素 */
        line-height: 48px; /* 设置行高,这样文字就垂直居中了 */
      }

      .nav a {
        display: block;
        font-size: 18px;
        color: #777777;
        text-decoration: none;
        padding: 0 38px; /* 让文字有一定的距离 */
      }

      /* 超链接悬浮效果 */
      .nav li a:hover {
        background-color: #3f3f3f;
        color: #e8e7e3;
      }
    </style>
  </head>

  <body>
    <ul class="nav">
      <li><a href="#">前端</a></li>
      <li><a href="#">后端</a></li>
      <li><a href="#">数据库</a></li>
    </ul>
  </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
  • 导航条和之前学习浮动的时候,效果是一样的,这里进行了简化。

显示如下:

因为要显示下拉菜单,菜单是根据导航按钮来的,所以将菜单添加到导航按钮的 <a> 标签中:

<li>
  <a class="database" href="#">
    数据库
    <div class="database-menu">
      <div>MySQL</div>
      <div>Oracle</div>
      <div>SQL Server</div>
    </div>
  </a>
</li>
1
2
3
4
5
6
7
8
9
10
  • 为了方便后面的操作,这里给 <a> 添加了一个 class

添加完成,显示如下:

下面开始设置样式,下拉菜单不能影响别的元素的布局,所以要将下拉菜单脱离文档流,直接将下来菜单使用绝对定位,父元素 <a> 使用相对定位,也就是 父相子绝 ,并设置下拉列表的样式。

.database {
  position: relative;  /* 父元素相对定位 */
}

.database-menu {
  display: block;
  width: 130px;
  font-size: 14px;
  line-height: 30px;
  color: #777777;
  background-color: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  position: absolute;  /* 子元素绝对定位 */
  left: 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

显示如下:

差不多了,设置一下下拉列表中每个选项的样式,和鼠标悬浮的样式:

.database-menu div {
  padding: 10px;
}
.database-menu div:hover {
  background-color: #CCCCCC;
}
1
2
3
4
5
6

显示如下:

下面添加一下小三角,通过 <a> 标签的伪元素添加就可以了:

.database::after {
  display: block;
  content: '';
  width: 0;
  height: 0;
  border: 10px solid transparent;
  border-top: none;
  border-bottom-color: white;
  position: absolute;
  margin-left: auto;    /* 为了让三角居中 */
  margin-right: auto;
  left: 0;
  right: 0;
  bottom: 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

显示如下:

现在显示的样式已经差不多了,现在调整一下显示,鼠标不悬浮的时候隐藏,鼠标悬浮的时候显示,将上面的 display 设置为 none; ,然后添加如下样式:

.database:hover .database-menu{
  display: block;
}
.database:hover::after {
  display: block;
}
1
2
3
4
5
6

完整代码如下:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style/reset.css" />
    <style>
      .nav {
        width: 1000px;
        height: 48px;
        background-color: #e8e7e3;
        margin: 50px auto; /* 左右margin auto,设置导航条居中,盒子模型章节讲过 */
      }

      /* li整体布局 */
      .nav li {
        float: left; /* 浮动li元素 */
        line-height: 48px; /* 设置行高,这样文字就垂直居中了 */
      }

      .nav a {
        display: block;
        font-size: 18px;
        color: #777777;
        text-decoration: none;
        padding: 0 38px; /* 让文字有一定的距离 */
      }

      /* 超链接悬浮效果 */
      .nav li a:hover {
        background-color: #3f3f3f;
        color: #e8e7e3;
      }

      .database {
        position: relative;
      }

      .database-menu {
        display: none;
        width: 130px;
        font-size: 14px;
        line-height: 30px;
        color: #777777;
        background-color: white;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
        position: absolute;
        left: 0;
      }

      .database-menu div {
        padding: 10px;
      }
      .database-menu div:hover {
        background-color: #CCCCCC;
      }

      .database::after {
        display: none;
        content: '';
        width: 0;
        height: 0;
        border: 10px solid transparent;
        border-top: none;
        border-bottom-color: white;
        position: absolute;
        margin-left: auto;    /* 为了让三角居中 */
        margin-right: auto;
        left: 0;
        right: 0;
        bottom: 0;
      }

      .database:hover .database-menu{
        display: block;
      }
      .database:hover::after {
        display: block;
      }
    </style>
  </head>

  <body>
    <ul class="nav">
      <li><a href="#">前端</a></li>
      <li><a href="#">后端</a></li>
      <li>
        <a class="database" href="#">
          数据库
          <div class="database-menu">
            <div>MySQL</div>
            <div>Oracle</div>
            <div>SQL Server</div>
          </div>
        </a>
      </li>
    </ul>
  </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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

# 9.13 思考

浮动可以布局,定位也可以布局,那什么时候使用浮动布局,什么时候使用定位?

一般在页面上进行大的布局的时候,例如整个页面的结构搭建,使用浮动来布局,当然,现在浮动使用的越来越少了,后面会讲到弹性盒布局。在小区域的模块布局、元素需要叠在一起,可以使用定位来调整。