ShiningDan的博客

网页布局笔记

在该笔记中,将学到的是多种不同的网页风格的实现或者多种不同风格的组件实现方法

网页布局基础

使用 CSS 实现不同的网页布局,其中最重要的是理解 CSS 中的 3 种定位机制,分别是:

  1. 标准文档流( Normal flow ):从左到右,从上到下,输出文档的内容。由块级元素和行级元素组成
  2. 浮动( Floats )
  3. 绝对定位( Absolute position )

块级元素:从左到右撑满页面,独占一行,碰到页面边缘会自动换行。(div, ul, li, dl, dt, p …)

行级元素:在同一行内显示,不会独占一行,不会改变文档的结构。(span, strong, img, input …)

块级元素和行级元素都是盒子模型的基础

盒(框)模型示意

自动居中一列布局

实现方法:标准文档流,块级元素,margin 属性

首先定义网页的一列布局:比如网页的布局是 header,mainbody 和 footer,则定义代码与显示如下:

<html>
    <head>
        <title>盒模型练习</title>
        <meta charset="utf-8">
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            #header{
                height:120px;
                width:100%;
                border:2px solid red;
            }
            #mainbody{
                height:120px;
                width:100%;
                border:2px solid blue;
            }
            #footer{
                height:120px;
                width:100%;
                border:2px solid green;
            }
            .content{
                border:4px solid #badbdb;
            }
            img{
                margin:10px 18px;
                width:80px;
                height:80px;
                border:1px solid gray; 
            }
        </style>
    </head>
    <body>
        <div id="header"></div>
        <div id="mainbody">
            <div class="content">
                <img src="./images/image1.jpg">
                <img src="./images/image1.jpg">
                <img src="./images/image1.jpg">
                <img src="./images/image1.jpg">
                <img src="./images/image1.jpg">
            </div>
        </div>
        <div id="footer"></div>
    </body>
</html>

显示效果如下:

当需要把一列布局自动居中,则需要使用一个包裹 div 将需要居中的内容进行包裹,然后设置 div#wrap 的 width 和左右的 margin auto:

<html>
    <head>
        <title>盒模型练习</title>
        <meta charset="utf-8">
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            #wrap{
                width:770px;
                margin:0 auto;
            }
            #header{
                height:120px;
                width:100%;
                border:2px solid red;
            }
            #mainbody{
                height:120px;
                width:100%;
                border:2px solid blue;
            }
            #footer{
                height:120px;
                width:100%;
                border:2px solid green;
            }
            .content{
                border:4px solid #badbdb;
            }
            img{
                margin:10px 18px;
                width:80px;
                height:80px;
                border:1px solid gray; 
            }
        </style>
    </head>
    <body>
        <div id="wrap">
            <div id="header"></div>
            <div id="mainbody">
                <div class="content">
                    <img src="./images/image1.jpg">
                    <img src="./images/image1.jpg">
                    <img src="./images/image1.jpg">
                    <img src="./images/image1.jpg">
                    <img src="./images/image1.jpg">
                </div>
            </div>
            <div id="footer"></div>
        </div>
    </body>
</html>

这个时候,被 wrap 框住的内容的 width 被设置成 770px,并且被自动居中。如果不设置 wrap 的 width,则默认的宽度为 100%,则自动居中没有效果。

显示效果如下:

解释:当 margin 被设置成 auto 的时候,它的宽度的就会根据浏览器的宽度进行自动的计算,计算原理为 (浏览器的宽度-外包含层的宽度)/2=外边距

但是如果 wrap 设置了浮动(float)或者定位属性(position),或者则自动居中将没有效果。


横向两列布局

浮动属性 float 的一些表现

使用浮动属性(float)可以实现块级元素的横向多列布局

float 可以设置的属性值有 left,right 和 none。设置的 float 的块元素的特点是元素会左移或者右移,直到触碰到其他的容器为止。设置了浮动属性的元素,仍处于标准文档流之中,并且会对紧邻它的元素位置产生影响(如果紧邻元素也是块元素,则该块元素不会独占一行,而是与前一元素合并为同一行)。

例如:

<html>
    <head>
        <title>float测试</title>
        <meta charset="utf-8">
        <style type="text/css">
            #box1{
                background-color:red;
                height:50px;
                float:left;
                width:200px;
            }
            #box2{
                background-color:blue;
                height:50px;
            }
        </style>
    </head>
    <body>
        <div id="box1"></div>
        <div id="box2"></div>
    </body>
</html>

显示效果如下:

注意:设置的 float 属性的元素,会自动压缩元素的宽度。如果元素中没有内容或者元素没有设置宽度,则元素会被自动压缩成一个小圆点。也就是浮动元素的宽度随着内容的变化而变化

例如:

<html>
    <head>
        <title>float测试</title>
        <meta charset="utf-8">
        <style type="text/css">
            #box1{
                background-color:red;
                height:50px;
                float:left;
            }
            #box2{
                background-color:green;
                height:50px;
                float:right;
            }
        </style>
    </head>
    <body>
        <p>this is a para</p>
        <div id="box1">box1box1box1</div>
        <div id="box2">box2box2</div>
        <p>this is a para</p>
    </body>
</html>

显示效果如下:

为了消除上一个元素的浮动属性对下一个元素造成影响的情况出现,我们需要清除浮动。

清除浮动的方法

为了消除上一个元素的浮动属性对下一个元素造成影响的情况出现,我们需要清除浮动。下面有两种清除浮动的方法

  1. 可以设置clean属性:通过 clear:both 命令可以清除受到影响的元素的浮动,如果明确地知道浮动受到影响的方向,也可以使用 clear:left 或者 clear:right
  2. 可以同时设置 width:100%(或者固定宽度) + overflow:hidden

下面演示的是使用 clear:both 来清除浮动:

<html>
    <head>
        <title>float测试</title>
        <meta charset="utf-8">
        <style type="text/css">
            #box1{
                background-color:red;
                height:50px;
                float:left;
            }
            #box2{
                background-color:green;
                height:50px;
                float:right;
            }
            #secondp{
                clear:both;
            }
        </style>
    </head>
    <body>
        <p>this is a para</p>
        <div id="box1">box1box1box1</div>
        <div id="box2">box2box2</div>
        <p id="secondp">this is a para</p>
    </body>
</html>

显示效果如下:

两种清除浮动方法的选择

如果浮动元素没有紧邻的下一个元素,则需要对包裹该浮动元素的父元素清除浮动,否则会发生父元素崩塌的情况出现(父元素的范围无法包裹子元素)。

如果在父元素上使用 clear:both 清除浮动,结果无效!:

<html>
    <head>
        <title>横向两列布局</title>
        <meta charset="utf-8">
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            #wrap{
                margin:0 auto;
                width:540px;
                border:2px solid black;
            }
            #header{
                height:100px;
                background-color:red;
            }
            #footer{
                height:100px;
                background-color:green;
            }
            #mainbody{
                clear:both;
                border:2px solid blue;
            }
            #mainleft{
                height:200px;
                width:400px;
                float:left;
                background-color:red;
            }
            #mainright{
                height:200px;
                width:100px;
                float:left;
                background-color:green;
            }
        </style>
    </head>
    <body>
        <div id="wrap">
            <div id="header"></div>
            <div id="mainbody">
                <div id="mainleft"></div>
                <div id="mainright"></div>            
            </div>
            <div id="footer"></div>
        </div>
    </body>
</html>

显示效果如下:

如图所示,父元素并没有成功地包裹住子元素。

所以在这时,只能使用 overflow:hidden 来对父元素清除浮动:

<html>
    <head>
        <title>横向两列布局</title>
        <meta charset="utf-8">
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            #wrap{
                margin:0 auto;
                width:540px;
                border:2px solid black;
            }
            #header{
                height:100px;
                background-color:red;
            }
            #footer{
                height:100px;
                background-color:green;
            }
            #mainbody{
                clear:both;
                border:2px solid blue;
                overflow:hidden;
            }
            #mainleft{
                height:200px;
                width:400px;
                float:left;
                background-color:red;
            }
            #mainright{
                height:200px;
                width:100px;
                float:left;
                background-color:green;
            }
        </style>
    </head>
    <body>
        <div id="wrap">
            <div id="header"></div>
            <div id="mainbody">
                <div id="mainleft"></div>
                <div id="mainright"></div>            
            </div>
            <div id="footer"></div>
        </div>
    </body>
</html>

显示效果如下:

如图所示,父元素 mainbody 成功地包裹住了子元素 mainleft 和 mainright。

横向两列布局的实现

实现方法:float,margin

使用 float 属性使纵向排列的块级元素横向排列

使用 margin 属性设置两列之间的距离

<html>
    <head>
        <title>横向两列布局</title>
        <meta charset="utf-8">
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            #wrap{
                margin:0 auto;
                width:540px;
                border:2px solid black;
            }
            #header{
                height:100px;
                background-color:red;
            }
            #footer{
                height:100px;
                background-color:green;
            }
            #mainbody{
                clear:both;
                border:2px solid blue;
                overflow:hidden;
            }
            #mainleft{
                height:200px;
                width:400px;
                float:left;
                background-color:red;
                margin-right:36px;
            }
            #mainright{
                height:200px;
                width:100px;
                float:right;
                background-color:green;
            }
        </style>
    </head>
    <body>
        <div id="wrap">
            <div id="header"></div>
            <div id="mainbody">
                <div id="mainleft"></div>
                <div id="mainright"></div>            
            </div>
            <div id="footer"></div>
        </div>
    </body>
</html>

显示效果如下:

比较推荐 mainleft 和 mainright 不设置 height,这样可以根据内容高度的大小进行自动包裹内容。

一些可以参考的使用横向两列布局的网站:腾讯软件中心


使用绝对定位实现横向两列布局

使用 position 属性实现定位,可以实现很多复杂的定位效果

position 的定位效果有三种:静态定位,相对定位和绝对定位。

静态定位:元素任然处于标准文档流中
相对定位:相对于自身原有的位置进行偏移,元素任然处于标准文档流中,当设置了相对属性,元素即拥有偏移属性(top,bottom,left,right)和 z-index 属性(空间位置的堆叠)
绝对定位:建立了以包含快为基准的定位,完全脱离了标准文档流,元素即拥有偏移属性(top,bottom,left,right)和 z-index 属性(空间位置的堆叠)

position 的属性可以是:

  1. static(静态定位,默认值)
  2. relative(相对定位)
  3. absolute(绝对定位)
  4. fixed(固定定位),也属于绝对定位

position:relative 的显示效果

  1. 相对于自身原有的位置进行偏移
  2. 元素任然处于标准文档流中
  3. 当设置了相对属性,元素即拥有偏移属性(top,bottom,left,right)和 z-index 属性(空间位置的堆叠)

一般使用相对定位 position:relative 作为父包含块的定位基准属性

<html>
    <head>
        <title>Position Relative演示</title>
        <style type="text/css">
            #box1{
                background-color: red;
                height:100px;
                position:relative;
                top:25px;
                left:25px;
            }
            #box2{
                background-color: green;
                height:100px;
            }
            #box3{
                background-color: blue;
                height:100px;
            }
        </style>
    </head>
    <body>
        <div id="box1">box1</div>
        <div id="box2">box2</div>
        <div id="box3">box3</div>
    </body>
</html>

显示效果如下:

position:absolute 的显示效果

  1. 建立了以包含快为基准的定位
  2. 完全脱离了标准文档流
  3. 元素即拥有偏移属性(top,bottom,left,right)和 z-index 属性(空间位置的堆叠)

当元素没有设置偏移量的时候

无论是否存在已经定位了的祖先元素,该元素都会保持在元素的初始位置。并且宽度随着内容的变化而变化。

<html>
    <head>
        <title>Position Relative演示</title>
        <style type="text/css">
            #box1{
                background-color: red;
                height:50px;
                position:absolute;
            }
            #box2{
                background-color: green;
                height:100px;
            }
            #box3{
                background-color: blue;
                height:100px;
            }
        </style>
    </head>
    <body>
        <div id="box1">box1</div>
        <div id="box2">box2</div>
        <div id="box3">box3</div>
    </body>
</html>

显示效果如下:

当元素设置了偏移量的时候

如果没有已经定位的祖先元素

会以 html 为偏移参照的基准。

<html>
    <head>
        <title>Position Relative演示</title>
        <style type="text/css">
            #box1{
                background-color: red;
                height:100px;
            }
            #wrap{
                height:100px;
                border:1px solid black;
            }
            #box2{
                background-color: green;
                height:50px;
                width:50px;
                position:absolute;
                top:20px;
            }
            #box3{
                background-color: blue;
                height:100px;
            }
        </style>
    </head>
    <body>
        <div id="box1">box1</div>
        <div id="wrap">
            <div id="box2">box2</div>
        </div>
        <div id="box3">box3</div>
    </body>
</html>

显示效果如下:

如果有已经定位的祖先元素

会以距离其最近的已经定位的祖先元素为偏移参照的基准。

<html>
    <head>
        <title>Position Relative演示</title>
        <style type="text/css">
            #box1{
                background-color: red;
                height:100px;
            }
            #wrap{
                height:100px;
                border:1px solid black;
                position:relative;
            }
            #box2{
                background-color: green;
                height:50px;
                width:50px;
                position:absolute;
                top:20px;
            }
            #box3{
                background-color: blue;
                height:100px;
            }
        </style>
    </head>
    <body>
        <div id="box1">box1</div>
        <div id="wrap">
            <div id="box2">box2</div>
        </div>
        <div id="box3">box3</div>
    </body>
</html>

显示效果如下:

横向两列布局的实现

使用绝对定位进行横向两列布局,使用的频率比较少,但是有合适的应用场合:常用于一列固定宽度,另一列自适应宽度的情况。

实现方法:

  1. relative:父元素相对定位
  2. absolute:自适应宽度的元素进行绝对定位

注意点:固定宽度的列的高度 > 自适应宽度的列的高度。因为使用绝对定位的元素,脱离了文档流,无法撑开父元素,所以需要在父元素中有一个相同高度,或者更高的元素撑开父元素。

在下面的例子中,sidebar 的宽度是绝对的,然后 content 是自适应宽度。需要给父元素 mainbody 设置 position:relative,然后 content 设置 position:absolute,并且使用 top:0,将 content 和 sidebar 放置在统一高度。最后给 content 设置 margin-left = sidebar的宽度,就完成了横向两列布局的实现:

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
<html>
<head>
<title>使用绝对定位实现横向两列式布局</title>
<meta charset="utf-8">
<style type="text/css">
*{
margin:0;
padding:0;
}
#wrap{
margin:0 auto;
width:540px;
border:2px solid black;
}
#header{
height:100px;
background-color:red;
}
#footer{
height:100px;
background-color:green;
}
#mainbody{
clear:both;
border:2px solid blue;
position:relative;
width:100%;
}
#sidebar{
height:200px;
width:100px;
background-color:red;
}
#content{
height:200px;
width:428px;
background-color:green;
position:absolute;
top:0;
margin-left:110px;
}
</style>
</head>
<body>
<div id="wrap">
<div id="header"></div>
<div id="mainbody">
<div id="sidebar">sidebar</div>
<div id="content">content</div>
</div>
<div id="footer"></div>
</div>
</body>
</html>

显示效果如下:


两栏等高布局

给两个元素用 padding 来填充,然后用 margin 来消除 padding 带来的影响,最后给父元素加一个 width:100%(或者固定宽度),overflow:hidden; 来清除浮动。

具体的实践过程中可以把 margin-bottom 和 padding-bottom 的值设大一些,要保证任意两栏的高度差不小于你设置的值。

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>左右宽度是否一样</title>
<style type="text/css">
body{
padding: 0;
margin: 0;
color: #f00;
}
.container{
width: 600px;
margin: 0 auto;
border: 3px solid #00c;
overflow: hidden;
}
.left{
width: 150px;
float: left;
background: #b0b0b0;
padding-bottom: 1000px;
margin-bottom: -1000px;
}
.right{
width: 450px;
float: left;
background: #6cc;
padding-bottom: 1000px;
margin-bottom: -1000px;
}
</style>
</head>
<body>
<div class="container">
<div class="left">我是左边</div>
<div class="right">我是右边<br/>现在我的高度比左边的高度要高哦。啊哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈啊哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>三栏式布局</title>
<style type="text/css">
* {margin:0; padding: 0px;}
.wrap{position: relative;}
.left,.right{height:100%; width: 200px; position: absolute; top: 0px;}
.left{background-color: red;left: 0;}
.right{background-color: green;right: 0}
.center{background-color: blue;margin: 0 210px;height: 500px;}
</style>
</head>
<body>
<div class="wrap">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
</body>
</html>

显示效果如下:

中间一栏的宽度是自适应的,通过中间一栏的高度来撑开父元素的高度。

这里的左中右三个div的顺序是可以任意调整的,这与剩下的两中方法就不一样了

margin负值法

这种方法是在实际的网站中应用的最多的

外层div宽度100%显示,并且浮动(本例左浮动,下面所述依次为基础),内层div为真正的主体内容,含有左右210像素的margin值。左栏与右栏都是采用margin负值定位的,左栏左浮动,margin-left为-100%,由于前面的div宽度100%与浏览器,所以这里的-100%margin值正好使左栏div定位到了页面的左侧;右侧栏也是左浮动,其margin-left也是负值,大小为其本身的宽度即200像素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>三栏式布局</title>
<style type="text/css">
* {margin: 0px;padding: 0px;}
body{height: 500px;}
.main{width: 100%;height: 100%;float: left;}
.main .body{background-color: red;height: 100%; margin: 0 210px;}
.left,.right{width: 200px;height: 100%;float: left;}
.left{margin-left: -100%;background-color: green;} /*把自己推到 .main 之前*/
.right{margin-left: -200px;background-color: blue;} /*把自己放在 .main 同一行之后*/
</style>
</head>
<body>
<div class="main"><div class="body"></div></div>
<div class="left"></div>
<div class="right"></div>
</body>
</html>

您需要注意几个div的顺序,无论是左浮动还是右浮动,先是主体部分div,这是肯定的,至于左右两栏谁先谁后,都无所谓

此方法的优点:三栏相互关联,可谓真正意义上的自适应,有一定的抗性——布局不易受内部影响。

缺点在于:相对比较难理解些,上手不容易,代码相对复杂。出现百分比宽度,过多的负值定位,如果出现布局的bug,排查不易。

自身浮动法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>三栏式布局</title>
<style type="text/css">
* {margin: 0px; padding: 0px;}
body {height: 500px;}
.left,.right {height: 100%; width: 200px;}
.left {background-color: red;float: left;}
.right {background-color: green; float: right;}
.main {background-color: blue; height: 100%;margin: 0 210px;}
</style>
</head>
<body>
<div class="left"></div>
<div class="right"></div>
<div class="main"></div>
</body>
</html>

这里三个div标签的顺序的关键是要把主体div放在最后,左右两栏div顺序任意。

因为使用浮动:

浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。

由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一

所以如果 .main.left.right 之前,left/right 会被 main 的边框碰到,无法下移到和 main 同一高度。