最全面的css布局
1.定位
定位的概念就是它允许你定义一个元素相对于其他正常元素的位置,它应该出现在哪里,这里的其他元素可以是父元素,另一个元素甚至是浏览器窗口本身。还有就是浮动了,其实浮动并不完全算是定位,它的特性非常的神奇,以至于它在布局中被人广泛的应用。我们会在后文中专门提及它的。
谈及定位,我们就得从position属性说起。你能准确的说出position的属性值吗?相信你可以完美地说出这么六个属性值:static、relative、absolute、fixed、sticky和inherit。
- static(默认):元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分;行内元素则会创建一个或多个行框,置于其父元素中。
- relative:元素框相对于之前正常文档流中的位置发生偏移,并且原先的位置仍然被占据。发生偏移的时候,可能会覆盖其他元素。
- absolute:元素框不再占有文档流位置,并且相对于包含块进行偏移(所谓的包含块就是最近一级外层元素position不为static的元素)
- fixed:元素框不再占有文档流位置,并且相对于视窗进行定位
- sticky:(这是css3新增的属性值)粘性定位,官方的介绍比较简单,或许你不能理解。其实,它就相当于relative和fixed混合。最初会被当作是relative,相对于原来的位置进行偏移;一旦超过一定阈值之后,会被当成fixed定位,相对于视口进行定位。
简单地,介绍一下position的属性值的含义后,在来看一下偏移量top、right、bottom、left四个属性。
不清楚,当初在初学css的时候,会不会与margin这个属性混淆?其实,它们之间是很容易去辨识地。因为这四个属性值,其实是,定位时的偏移量。偏移量不会对static的元素起到作用。而margin,相对应的是盒子模型的外边距,它会对每个元素框起到作用,使得元素框与其他元素之间产生空白。
下面:我们来看一下一些常用定位的偏移
- relative:它的偏移是相对于原先在文档流中的位置
- absolute:它的偏移量是相对于最近一级position不是static的祖先元素的
- fixed:它的偏移量是相对于视口的。
其实,这里说描述的内容,应该都是需要理解的。这些相对于布局来说是基础的,同时也是非常重要的。需要注意的是,这里的偏移量其实已经涉及到了接下来要说的尺寸。在做自适应布局设计时,往往希望这些偏移量的单位能够使用百分比,或者相对的单位例如rem等。
2.尺寸
那之前上面谈到过尺寸的单位——百分比。那么,下面部分我们就围绕着尺寸单位展开。
尺寸,我们就应该从单位聊起,对于px这个单位,做网页的应该在熟悉不过了,因此不多做介绍。
那么,我们可以来介绍一下下面几个单位:
- 百分比:百分比的参照物是父元素,50%相当于父元素width的50%
- rem:这个对于复杂的设计图相当有用,它是html的font-size的大小
- em:它虽然也是一个相对的单位,相对于父元素的font-size,但是,并不常用,主要是计算太麻烦了。
单位只是一个来定义元素大小的相应参考。另一个概念,或许可以用房子来打一个比方,在早年每幢房子都会在房子的外围建一层栅栏,使得整一块地区可以看成房子+内部地块+栅栏+外围地块的模型。而在css中,每个元素也会有盒子模型的概念。
盒子模型:每个元素,都会形成一个矩形块,主要包括四部分:margin(外边距)+border(边框)+padding(内边距)+content(内容)
css中存在两种不同的盒子模型,可以通过box-sizing设置不同的模型。两种盒子模型,主要是width的宽度不同
这是标准盒子模型,可以看到width的长度等于content的宽度;而当将box-sizing的属性值设置成border-box时,盒子模型的width=border+padding+content的总和。
可以看出,对于不同的模型的宽度是不同的。宽度默认的属性值是auto,这个属性值会使得内部元素的长度自动填充满父元素的width。
但是,height的属性值也是默认的auto,为什么没有像width一样呢?
其实,auto这个属性值表示的是浏览器自动计算。这种自动计算,需要一个基准,一般浏览器都是允许高度滚动的,所以,会导致一个问题——浏览器找不到垂直方向上的基准。
同样地道理也会被应用在margin属性上。相信如果考察居中时,水平居中你可能闭着眼睛都能写出来,但是垂直居中却绕着脑袋想。这是因为如果是块级元素水平居中只要将水平方向上的margin设置成auto就可以了。但是,垂直方向上却没有这么简单,因为你设置成auto时,margin为0。这个问题,还是需要仔细思考一下的。
到此为止,布局最基本的部分我们已经将去大半,还有就是一块浮动。
3.浮动
浮动,这是一个非常有意思的东西,在布局中被广泛的应用。最初,设计浮动时,其实并不是为了布局的,而是为了实现文字环绕的特效
但是,浮动并不是仅仅这样而已。何为浮动?浮动应该说是‘自成一派’,类似于ps中的图层一样,浮动的元素会在浮动层上面进行排布,而在原先文档流中的元素位置,会被以某种方式进行删除,但是还是会影响布局。你可能会觉得有疑问,什么叫影响布局?我们可以来举个例子:
可以,发现虽然left块因为左浮动,而使得原先元素在文档流中占有的位置被删除,但是,当right块补上原先的位置时,right块中的字体却被挤出来了。这就是所谓的影响布局。
浮动为什么会被使用在布局中呢?因为设置浮动后的元素会形成BFC(使得内部的元素不会被外部所干扰),并且元素的宽度也不再自适应父元素宽度,而是适应自身内容。这样就可以,轻松地实现多栏布局的效果。
浮动的内容还需要介绍一块——清除浮动。可以看到,浮动元素,其实对于布局来说,是特别危险的。因为你可能这一块做过浮动,但未做清除,那么造成高度塌陷的问题。就是上面图示的那种情况。
清除浮动,最常用的方法有两种:
- overflow: 将父元素的overflow,设置成hidden。
- after伪类:对子元素的after伪类进行设置。
这里只是稍微的提上一嘴。下面我们正式来介绍一下网页的布局,本篇最核心的东西。
4.最初的布局——table
最初的时候,网页简单到可能只有文字和链接。这时候,大家最常用的就是table。因为table可以展示出多个块的排布。
这种布局现在应该不常用了,因为在形色单一时,使用起来方便。但是,现在的网页变得越来越复杂,适配的问题也是越来越多,这种布局已经不再时候了。
主要是div块的出现,可以使得网页进行灵活的排布,使得网页变得繁荣。这时,开发者也开始思索如何去更加清晰地分辨网页的层次。接下来,我们可以看看有哪些比较出名的布局方式。
5.两栏布局
是否记得,那些一边主体内容,一边目录的网页,如图:
类似于上图的布局,可以定义为两栏布局。
两栏布局:一栏定宽,一栏自适应。这样子做的好处是定宽的那一栏可以做广告,自适应的可以作为内容主体。
实现的方式:
- float + margin:
<body> <div class="left">定宽</div> <div class="right">自适应</div> </body> .left{ width: 200px; height: 600px; background: red; float: left; display: table; text-align: center; line-height: 600px; color: #fff; } .right{ margin-left: 210px; height: 600px; background: yellow; text-align: center; line-height: 600px; }
其他的方法:还可以使用position的absolute,可以使用同样的效果
6.三栏布局
三栏布局,也是经常会被使用到的一种布局。
它的特点:两边定宽,然后中间的width是auto的,可以自适应内容,再加上margin边距,来进行设定。
三栏布局可以有4种实现方式,每种实现方式都有各自的优缺点。
1.使用左右两栏使用float属性,中间栏使用margin属性进行撑开,注意的是html的结果
<div class="left">左栏</div> <div class="right">右栏</div> <div class="middle">中间栏</div> .left{ width: 200px;height: 300px; background: yellow; float: left; } .right{ width: 150px; height: 300px; background: green; float: right; } .middle{ height: 300px; background: red; margin-left: 220px; margin-right: 160px; }
缺点是:1. 当宽度小于左右两边宽度之和时,右侧栏会被挤下去;2. html的结构不正确
2. 使用position定位实现,即左右两栏使用position进行定位,中间栏使用margin进行定位
<div class="left">左栏</div> <div class="middle">中间栏</div> <div class="right">右栏</div> .left{ background: yellow; width: 200px; height: 300px; position: absolute; top: 0; left: 0; } .middle{ height: 300px; margin: 0 220px; background: red; } .right{ height: 300px; width: 200px; position: absolute; top: 0; right: 0; background: green; }
好处是:html结构正常。
缺点时:当父元素有内外边距时,会导致中间栏的位置出现偏差
3. 使用float和BFC配合圣杯布局
将middle的宽度设置为100%,然后将其float设置为left,其中的main块设置margin属性,然后左边栏设置float为left,之后设置margin为-100%,右栏也设置为float:left,之后margin-left为自身大小。
<div class="wrapper"> <div class="middle"> <div class="main">中间</div> </div> <div class="left"> 左栏 </div> <div class="right"> 右栏 </div> </div> .wrapper{ overflow: hidden; //清除浮动 } .middle{ width: 100%; float: left; } .middle .main{ margin: 0 220px; background: red; } .left{ width: 200px; height: 300px; float: left; background: green; margin-left: -100%; } .right{ width: 200px; height: 300px; float: left; background: yellow; margin-left: -200px; }
7.弹性布局(flex)
弹性盒子(Flexbox)布局是一种为一维布局而设计的布局方法。一维的意思是你希望内容是按行或者列来布局。你可以使用display: flex
来将元素变为弹性布局。
.container { display: flex; }
<div class="container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> </div>
该容器的直接子元素会变为弹性项(flex item),并按行排列。
如图:
7.1. 弹性盒子的轴(axes)
在上面的例子中,我们会称弹性项在行内是从起始位置开始排列,而不是说它们是左对齐。这些元素会按行排列是因为默认的flex-direction
值为row
,row
代表了文本的行文方向。由于我们工作的环境是英文(中文也是如此),一种自左向右的语言,行的开始位置就是在左边,因此我们的弹性项也是从左边开始的。因此flex-direction
的值被定义为弹性盒子的主轴(main axis)。
交叉轴(cross axis)则是和主轴垂直的一条轴。如果你的flex-direction
是row
并且弹性项是按照行内方向排列的,那么交叉轴就是块级元素的排列方向。如果flex-direction
是column
那么弹性项就会以块级元素排列的方向排布,然后交叉轴就会变为row
。
如果你习惯于从主轴与交叉轴的角度来使用弹性盒子,那么一切会变得非常简单。
7.2. 方向和次序
弹性盒子模型让我们可以通过为flex-direction
属性设置row-reverse
或column-reverse
值来改变主轴上弹性项的方向。
<div class="container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> </div>
body { padding: 20px; font: 1em Helvetica Neue, Helvetica, Arial, sans-serif; } * {box-sizing: border-box;} p { margin: 0 0 1em 0; } .container { width: 500px; border: 5px solid rgb(111,41,97); border-radius: .5em; padding: 10px; display: flex; flex-direction: row-reverse; } .item { width: 100px; height: 100px; padding: 10px; background-color: rgba(111,41,97,.3); border: 2px solid rgba(111,41,97,.5); }
7.3. 一些Flex的属性
这些flex的属性是用来控制弹性项在主轴上空间大小的。这三个属性是:
- flex-grow
- flex-shrink
- flex-basis
通常可以使用它们的简写形式:flex
。第一个值代表flex-grow
,第二个是flex-shrink
,而第三个则是flex-basis
。
.item { flex: 1 1 200px; }
flex-basis
会为弹性项设置未拉伸和压缩时的初始大小。在上面的例子中,大小是200px,因此我们会给每个项200px的空间大小。但是大多数情况下容器元素大小不会正好被分为许多200px大小的项,而是可能有一些不足或剩余空间。flex-grow
和flow-shrink
属性允许我们在容器大小不足或有空余时控制各个弹性项的大小。
如果flex-grow
的值是任意的正数,那么弹性项会被允许拉伸来占据更多的空间。因此,在上面的例子中,当各项被设为200px后,所有多余的空间会被每个弹性项平分并填满。
如果flex-shrink
的值为任意的正数,那么当弹性项被设置了flex-basis
后,元素溢出容器时会进行收缩。在上面这个CSS的例子中,如果容器空间不足,每个弹性项会等比例缩放以适应容器的大小。
flex-grow
和flex-shrink
的值可以是任意的正数。一个具有较大flex-grow
值的弹性项会在容器有剩余空间时拉伸更大的比例;而一个具有更大flex-shrink
值的项则会在容器空间不足时被压缩的更多。
1 <div class="container"> 2 <div class="item">1</div> 3 <div class="item">2</div> 4 <div class="item">3</div> 5 </div>
1 body { 2 padding: 20px; 3 font: 1em Helvetica Neue, Helvetica, Arial, sans-serif; 4 } 5 6 * {box-sizing: border-box;} 7 8 p { 9 margin: 0 0 1em 0; 10 } 11 12 .container { 13 width: 500px; 14 border: 5px solid rgb(111,41,97); 15 border-radius: .5em; 16 padding: 10px; 17 display: flex; 18 } 19 20 .item { 21 flex: 1 1 0; 22 width: 100px; 23 height: 100px; 24 padding: 10px; 25 background-color: rgba(111,41,97,.3); 26 border: 2px solid rgba(111,41,97,.5); 27 } 28 29 .container :first-child { 30 flex: 2 1 0; 31 }
8. 网格布局(grid layout)
CSS网格布局(grid layout)是一种用来进行二维布局的技术。二维(two-dimesional)意味着你希望按照行和列来排布你的内容。和弹性盒子类似,网格布局也需要设置一个display
值。你可以为容器元素设置display: grid
,并且使用grid-template-columns
和grid-template-rows
属性来控制网格中的行与列。
.container { display: grid; grid-template-columns: 200px 200px 200px; grid-template-rows: 200px 200px; }
上面这段CSS会创建一个行列元素大小固定的网格。不过这也许并不是你希望的。默认值为auto
,你可以认为这代表了“让格子尽可能的大”。如果你每没有指定行(row track)的大小,所有添加进来的行内容大小都会被置为auto
。一种常用的模式是为网格制定列宽度,但是允许网格按需添加行。
你可以使用任意的长度单位或时百分比来设置行与列,同时你可以使用为网格系统所创造的新的单位——fr
。fr
是一种弹性单位,它可以指定网格容器内的空间被如何划分。
网格会替你计算与分配空间,你不需要去计算元素的百分比去适应容器大小。在下面这个例子中,我们使用fr
来创建网格的列,这使得网格的列可以自适应。同时我们还使用了grid-gap
来保证元素间的间距(关于网格内元素与的间距会在“对齐”这一部分详细介绍)。
<div class="container"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5<br>has more content.</div> </div>
body { padding: 20px; font: 1em Helvetica Neue, Helvetica, Arial, sans-serif; } * {box-sizing: border-box;} p { margin: 0 0 1em 0; } .container { width: 500px; border: 5px solid rgb(111,41,97); border-radius: .5em; padding: 10px; display: grid; grid-template-columns: 1fr 1fr 1fr; grid-gap: 20px; } .container > div { padding: 10px; background-color: rgba(111,41,97,.3); border: 2px solid rgba(111,41,97,.5); }