一、定位
定位允许您从正常的文档流布局中取出元素,并使它们具有不同的行为,例如放在另一个元素的上面,或者始终保持在浏览器视窗内的同一位置。
要使某个元素拥有特定类型的定位,我们使用position
属性。
定位属性目前一共有五种类型:
- 静态定位
静态定位是每个元素获取的默认值——它只是意味着“将元素放入它在文档布局流中的正常位置 ——这里没有什么特别的。
...
对应的样式信息如下:
.positioned {
position: static;
background: yellow;
}
由于静态定位是每个元素的默认定位行为,当元素未设置定位时,显示书写该属性并不会对页面布局有什么改变,当然若你想要取消采用其他定位方式的元素的定位属性,可以加上这个定位方式。
- 相对定位
它与静态定位非常相似,占据在正常的文档流中,除了你仍然可以修改它的最终位置,包括让它与页面上的其他元素重叠。
position: relative;
那么如何修改元素的位置呢? 您需要使用top
,bottom
,left
和right
属性
top, bottom, left, right
我们可以使用top
, bottom
, left
, 和 right
来精确指定要将定位元素移动到的位置。如CSS中的 .positioned
规则中添加以下声明:
top: 30px;
left: 30px;
注意:这些属性的值可以采用逻辑上期望的任何单位 ——px,mm,rems,%等。
好吧,这个结果这可能不是你期待的——为什么它移动到底部和右边,但我们指定顶部和左边? 听起来不合逻辑,但这只是相对定位工作的方式——你需要考虑一个看不见的力,推动定位的盒子的一侧,移动它的相反方向。 所以例如,如果你指定 top: 30px;一个力推动框的顶部,使它向下移动30px。
- 绝对定位
(1). 绝对定位的元素不再存在于正常文档布局流中,元素之间的间隙不再存在。相反,它坐在它自己的层独立于一切。这是非常有用的:这意味着我们可以创建不干扰页面上其他元素的位置的隔离的UI功能 。例如,弹出信息框和控制菜单;翻转面板;可以在页面上的任何地方拖放的UI功能……
(2). 元素的位置已经改变——这是因为top
,bottom
,left
和right
以不同的方式在绝对定位。 它们指定元素应距离每个包含元素的边的距离,而不是指定元素应该移入的方向。 所以在这种情况下,我们说的绝对定位元素应该位于“包含元素”的顶部30px,从左边30px。
注意:外边距仍会影响定位的元素。 然而,外边距不会折叠。
定位上下文
哪个元素是绝对定位元素的“包含元素”? 默认情况下,它是元素(有的浏览器的实现方式是根据视口做定位)——定位的元素是被嵌套在
中的HTML源代码,但在最终的布局,它离页面边缘的顶部和左侧30px距离,这是
元素。 这更准确地称为元素的定位上下文。
我们可以改变定位上下文—— 这是通过在元素的其他祖先之一上设置定位来实现的(除了默认的static定位之外的所有类型定位,都可以)——绝对定位的元素会将离他最近的已定位的祖先元素作为定位上下文(你不能相对于其中没有嵌套的元素来定位它)。 如在body规则中添加相对定位(仅加入相对定位,不写left,top,right,bottom属性并不会对元素位置造成改变从而影响页面布局):
position: relative;
定位的元素现在相对于元素,即body元素成为了定位上下文。
z-index
当元素开始重叠,什么决定哪些元素出现在其他元素的顶部? 我们在定位上下文中只有一个定位的元素,它出现在顶部,因为定位的元素胜过未定位的元素。 当我们有不止一个的时候呢?
尝试添加以下到您的CSS,使第一段也是绝对定位:
p:nth-of-type(1) {
position: absolute;
background: lime;
top: 10px;
right: 30px;
}
此时,您将看到第一段的颜色为绿色,移出文档流程,并位于原始位置上方一点。它也堆叠在原始的 .positioned
段落下,其中两个重叠。这是因为 .positioned
段落是源顺序中的第二个段落,并且源顺序中后定位的元素将赢得先定位的元素。
您可以更改堆叠顺序吗?是的,您可以使用z-index
属性。 “z-index”是对z轴的参考。你可以从源代码中的上一点回想一下,我们使用水平(x轴)和垂直(y轴)坐标来讨论网页,以确定像背景图像和阴影偏移之类的东西的位置。 (0,0)位于页面(或元素)的左上角,x和y轴跨页面向右和向下(适合从左到右的语言,无论如何)。
网页也有一个z轴:一条从屏幕表面到你的脸(或者在屏幕前面你喜欢的任何其他东西)的虚线。z-index
值影响定位元素位于该轴上的位置;正值将它们移动到堆栈上方,负值将它们向下移动到堆栈中。默认情况下,定位的元素都具有z-index为auto,实际上为0。
要更改堆叠顺序,请尝试将以下声明添加到 p:nth-of-type(1)
规则中:
z-index: 1;
请注意,z-index只接受无单位索引值;你不能指定你想要一个元素是Z轴上23像素—— 它不这样工作。 较高的值将高于较低的值,这取决于您使用的值。 使用2和3将产生与300和40000相同的效果。
- 固定定位
这与绝对定位的工作方式完全相同,只有一个主要区别:绝对定位固定元素是相对于元素或其最近的定位祖先,而固定定位固定元素则是相对于浏览器视口本身。 这意味着您可以创建固定的有用的UI项目,如持久导航菜单。
让我们举一个简单的例子来说明我们的意思。首先,从CSS中删除现有的 p:nth-of-type(1)
和.positioned
规则。
现在,更新 body
规则以删除position: relative;
声明并添加固定高度,如此:
body {
width: 500px;
height: 1400px;
margin: 0 auto;
}
现在我们要给元素
position: fixed;
,并让它坐在视口的顶部中心。将以下规则添加到CSS:
h1 {
position: fixed;
top: 0;
width: 500px;
margin: 0 auto;
background: white;
padding: 10px;
}
top: 0;
是要使它贴在屏幕的顶部;我们然后给出标题与内容列相同的宽度,并使用可靠的老技巧 margin: 0 auto;
使它居中。 然后我们给它一个白色背景和一些内边距,所以内容将不会在它下面可见。
如果您现在保存并刷新,您会看到一个有趣的小效果,标题保持固定,内容显示向上滚动并消失在其下。 但是我们可以改进这一点——目前标题下面挡住一些内容的开头。这是因为定位的标题不再出现在文档流中,所以其他内容向上移动到顶部。 我们要把它向下移动一点;我们可以通过在第一段设置一些顶部边距来做到这一点。添加:
p:nth-of-type(1) {
margin-top: 60px;
}
- position: sticky
position: sticky
,是一个比其他属性要新一些的属性。这基本上是相对位置和固定位置之间的混合,其允许定位的元件像它被相对定位一样动作,直到其滚动到某一阈值点(例如,从视口顶部10像素),之后它变得固定。
二、弹性盒子
html代码:
Flexbox 0 — starting code
Sample flexbox example
First article
Tacos actually microdosing, pour-over semiotics banjo chicharrones retro fanny pack portland everyday carry vinyl typewriter. Tacos PBR&B pork belly, everyday carry ennui pickled sriracha normcore hashtag polaroid single-origin coffee cold-pressed. PBR&B tattooed trust fund twee, leggings salvia iPhone photo booth health goth gastropub hammock.
Second article
Tacos actually microdosing, pour-over semiotics banjo chicharrones retro fanny pack portland everyday carry vinyl typewriter. Tacos PBR&B pork belly, everyday carry ennui pickled sriracha normcore hashtag polaroid single-origin coffee cold-pressed. PBR&B tattooed trust fund twee, leggings salvia iPhone photo booth health goth gastropub hammock.
Third article
Tacos actually microdosing, pour-over semiotics banjo chicharrones retro fanny pack portland everyday carry vinyl typewriter. Tacos PBR&B pork belly, everyday carry ennui pickled sriracha normcore hashtag polaroid single-origin coffee cold-pressed. PBR&B tattooed trust fund twee, leggings salvia iPhone photo booth health goth gastropub hammock.
Cray food truck brunch, XOXO +1 keffiyeh pickled chambray waistcoat ennui. Organic small batch paleo 8-bit. Intelligentsia umami wayfarers pickled, asymmetrical kombucha letterpress kitsch leggings cold-pressed squid chartreuse put a bird on it. Listicle pickled man bun cornhole heirloom art party.
- 指定元素的布局为 flexible
首先,我们需要选择将哪些元素将设置为弹性的盒子。我们需要给这些 flexible 元素的父元素 display
设置一个特定值。如给 (变成了 flex 容器)设置 display:
section {
display: flex;
}
注意:假如你想设置行内元素为 flexible box,也可以置 display
属性的值为 inline-flex。
- flex 模型说明
当元素表现为 flex 框时,它们沿着两个轴来布局:
主轴(main axis)是沿着 flex 元素放置的方向延伸的轴(比如页面上的横向的行、纵向的列)。该轴的开始和结束被称为main start 和main end。
交叉轴(cross axis)是垂直于 flex 元素放置方向的轴。该轴的开始和结束被称为 cross start 和cross end。
设置了
display: flex
的父元素被称之为 flex 容器(flex container)。在 flex 容器中表现为弹性的盒子的元素被称之为 flex 项(flex item)。
- 列还是行?
弹性盒子提供了 flex-direction
这样一个属性,它可以指定主轴的方向(弹性盒子子类放置的地方)— 它默认值是 row
,这使得它们在按你浏览器的默认语言方向排成一排(在英语/中文浏览器中是从左到右)。
尝试将以下声明添加到 section 元素的 css 规则里:
flex-direction: column
你会看到,这会将那些元素设置为列布局
注意:你还可以使用 row-reverse 和 column-reverse 值反向排列 flex 项目。
- 换行
当你在布局中使用定宽或者定高的时候,可能会有一个问题出来即处于容器中的 弹性盒子子元素会溢出,破坏了布局。
解决此问题的一种方法是将以下声明添加到 section css 规则中:
flex-wrap: wrap
- flex-flow 缩写
到这里,应当注意到存在着 flex-direction
和 flex-wrap
— 的缩写 flex-flow
。比如,你可以将
flex-direction: row;
flex-wrap: wrap;
替换为
flex-flow: row wrap;
- flex 项的动态尺寸
第一步,将以下规则添加到 CSS 的底部:
article {
flex: 1;
}
这是一个无单位的比例值,表示每个 flex 项沿主轴的可用空间大小。本例中,我们设置 元素的 flex 值为 1,这表示每个元素占用空间都是相等的,占用的空间是在设置 padding 和 margin 之后剩余的空间。因为它是一个比例,这意味着将每个 flex 项的设置为 400000 的效果和 1 的时候是完全一样的。
现在在上一个规则下添加:
article:nth-of-type(3) {
flex: 2;
}
现在当你刷新,你会看到第三个 元素占用了两倍的可用宽度和剩下的一样 — 现在总共有四个比例单位可用。 前两个 flex 项各有一个,因此它们占用每个可用空间的1/4。 第三个有两个单位,所以它占用2/4或这说是1/2的可用空间。
您还可以指定 flex 的最小值。 尝试修改现有的 article 规则:
article {
flex: 1 200px;
}
article:nth-of-type(3) {
flex: 2 200px;
}
这表示“每个flex 项将首先给出200px的可用空间,然后,剩余的可用空间将根据分配的比例共享“。 尝试刷新,你会看到分配空间的差别。
弹性盒子的真正价值可以体现在它的灵活性/响应性,如果你调整浏览器窗口的大小,或者增加一个 元素,这时的布局仍旧是好的。
- flex: 缩写与全写
flex
是一个可以指定最多三个不同值的缩写属性:
- 第一个就是上面所讨论过的无单位比例。可以单独指定全写
flex-grow
属性的值。 - 第二个无单位比例 —
flex-shrink
— 一般用于溢出容器的 flex 项。这指定了从每个 flex 项中取出多少溢出量,以阻止它们溢出它们的容器。 这是一个相当高级的弹性盒子功能。 - 第三个是上面讨论的最小值。可以单独指定全写
flex-basis
属性的值。
我们建议不要使用全写属性,除非你真的需要(比如要去覆盖之前写的)。使用全写会多些很多的代码,它们也可能有点让人困惑。
6.水平和垂直对齐
还可以使用 弹性盒子的功能让 flex 项沿主轴或交叉轴对齐。
html部分:
Flexbox align 0 — starting code
然后,添加下面的 CSS :
div {
display: flex;
align-items: center;
justify-content: space-around;
}
你就会看到这些按钮很好的垂直水平居中了。我们是通过下面所说的两个新的属性做到的。
align-items
控制 flex 项在交叉轴上的位置。
- 默认的值是
stretch
,其会使所有 flex 项沿着交叉轴的方向拉伸以填充父容器。如果父容器在交叉轴方向上没有固定宽度(即高度),则所有 flex 项将变得与最长的 flex 项一样长(即高度保持一致)。我们的第一个例子在默认情况下得到相等的高度的列的原因。 - 在上面规则中我们使用的
center
值会使这些项保持其原有的高度,但是会在交叉轴居中。这就是那些按钮垂直居中的原因。 - 你也可以设置诸如
flex-start
或flex-end
这样使 flex 项在交叉轴的开始或结束处对齐所有的值。
你可以用 align-self
属性覆盖 align-items
的行为。比如,你可以这样:
button:first-child {
align-self: flex-end;
}
justify-content
控制 flex 项在主轴上的位置。
- 默认值是
flex-start
,这会使所有 flex 项都位于主轴的开始处。 - 你也可以用
flex-end
来让 flex 项到结尾处。 -
center
在justify-content
里也是可用的,可以让 flex 项在主轴居中。 - 而我们上面用到的值
space-around
是很有用的——它会使所有 flex 项沿着主轴均匀地分布,在任意一端都会留有一点空间。 - 还有一个值是
space-between
,它和space-around
非常相似,只是它不会在两端留下任何空间。
- flex 项排序
弹性盒子也有可以改变 flex 项的布局位置的功能,而不会影响到源顺序(即 dom 树里元素的顺序)。这也是传统布局方式很难做到的一点。
代码也很简单,将下面的 CSS 添加到示例代码下面。
button:first-child {
order: 1;
}
刷新下,然后你会看到 "Smile" 按钮移动到了主轴的末尾。下面我们谈下它实现的一些细节:
- 所有 flex 项默认的
order
值是 0。 - order 值大的 flex 项比 order 值小的在显示顺序中更靠后。
- 相同 order 值的 flex 项按源顺序显示。所以假如你有四个元素,其 order 值分别是2,1,1和0,那么它们的显示顺序就分别是第四,第二,第三,和第一。
- 第三个元素显示在第二个后面是因为它们的 order 值一样,且第三个元素在源顺序中排在第二个后面。
你也可以给 order 设置负值使它们比值为 0 的元素排得更前面。比如,你可以设置 "Blush" 按钮排在主轴的最前面:
button:last-child {
order: -1;
}
- flex 嵌套
弹性盒子也能创建一些颇为复杂的布局。设置 flex 项为 flex 容器也是没有什么问题的,它的孩子也就表现为 flexible box 了。
三、媒体查询
“响应式设计(Responsive Design” 是一种让网站针对不同的浏览器和设备“呈现”不同显示效果的策略,这样可以让网站在任何情况下显示的很棒!
媒体查询是做此事所需的最强大的工具。让我们使用百分比宽度来布局,然后在浏览器变窄到无法容纳侧边栏中的菜单时,把布局显示成一列:
@media screen and (min-width:600px) {
nav {
float: left;
width: 25%;
}
section {
margin-left: 25%;
}
}
@media screen and (max-width:599px) {
nav li {
display: inline;
}
}
四、左右布局
七种实现左侧固定,右侧自适应两栏布局的方法(转载)
总结一下左边固定,右边自适应的两栏布局的七种方法。其中有老生常谈的float
方法,BFC方法,也有CSS3的flex
布局与grid
布局。并非所有的布局都会在开发中使用,但是其中也会涉及一些知识点。关于最终的效果,可以查看这里
常用的宽度自适应的方法通常是利用了block
水平的元素宽度能随父容器调节的流动特性。另外一种思路是利用CSS中的calc()
方法来动态设定宽度。还有一种思路是,利用CSS中的新型布局flex layout
与grid layout
。
首先创建基本的HTML布局和最基本的样式。
左边固定宽度,高度不固定 高度有可能会很小,也可能很大。
这里的内容可能比左侧高,也可能比左侧低。宽度需要自适应。
基本的样式是,两个div相距20px, 左侧div宽 120px
基本的样式是,两个盒子相距20px
, 左侧盒子宽 120px
,右侧盒子宽度自适应。基本的CSS样式如下:
.wrapper {
padding: 15px 20px;
border: 1px dashed #ff6c60;
}
.left {
width: 120px;
border: 5px solid #ddd;
}
.right {
margin-left: 20px;
border: 5px solid #ddd;
}
下面的代码都是基于这套基本代码做覆盖,通过给容器添加不同的类来实现效果。
双inline-block
方案
.wrapper-inline-block {
box-sizing: content-box;
font-size: 0; // 消除空格的影响
}
.wrapper-inline-block .left,
.wrapper-inline-block .right {
display: inline-block;
vertical-align: top; // 顶端对齐
font-size: 14px;
box-sizing: border-box;
}
.wrapper-inline-block .right {
width: calc(100% - 140px);
}
这种方法是通过width: calc(100% - 140px)
来动态计算右侧盒子的宽度。需要知道右侧盒子距离左边的距离,以及左侧盒子具体的宽度(content+padding+border),以此计算父容器宽度的100%
需要减去的数值。同时,还需要知道右侧盒子的宽度是否包含border
的宽度。
在这里,为了简单的计算右侧盒子准确的宽度,设置了子元素的box-sizing:border-box;
以及父元素的box-sizing: content-box;
。
同时,作为两个inline-block
的盒子,必须设置vertical-align
来使其顶端对齐。
另外,为了准确地应用计算出来的宽度,需要消除div
之间的空格,需要通过设置父容器的font-size: 0;
,或者用注释消除html
中的空格等方法。
缺点:
需要知道左侧盒子的宽度,两个盒子的距离,还要设置各个元素的
box-sizing
需要消除空格字符的影响
需要设置
vertical-align: top
满足顶端对齐。
双float
方案
.wrapper-double-float {
overflow: auto; // 清除浮动
box-sizing: content-box;
}
.wrapper-double-float .left,
.wrapper-double-float .right {
float: left;
box-sizing: border-box;
}
.wrapper-double-float .right {
width: calc(100% - 140px);
}
本方案和双float
方案原理相同,都是通过动态计算宽度来实现自适应。但是,由于浮动的block
元素在有空间的情况下会依次紧贴,排列在一行,所以无需设置display: inline-block;
,自然也就少了顶端对齐,空格字符占空间等问题。
A floated box is shifted to the left or right until its outer edge touches the containing block edge or the outer edge of another float.
不过由于应用了浮动,父元素需要清除浮动。
缺点:
需要知道左侧盒子的宽度,两个盒子的距离,还要设置各个元素的
box-sizing
。父元素需要清除浮动。
float+margin-left
方案
.wrapper-float {
overflow: hidden; // 清除浮动
}
.wrapper-float .left {
float: left;
}
.wrapper-float .right {
margin-left: 150px;
}
上面两种方案都是利用了CSS的calc()
函数来计算宽度值。下面两种方案则是利用了block
级别的元素盒子的宽度具有填满父容器,并随着父容器的宽度自适应的流动特性。
但是block
级别的元素都是独占一行的,所以要想办法让两个block
排列到一起。
我们知道,block
级别的元素会认为浮动的元素不存在,但是inline
级别的元素能识别到浮动的元素。这样,block
级别的元素就可以和浮动的元素同处一行了。
为了让右侧盒子和左侧盒子保持距离,需要为左侧盒子留出足够的距离。这个距离的大小为左侧盒子的宽度以及两个盒子之间的距离之和。然后将该值设置为右侧盒子的margin-left
。
缺点:
需要清除浮动
需要计算右侧盒子的
margin-left
使用absolute+margin-left
方法
另外一种让两个block
排列到一起的方法是对左侧盒子使用position: absolute
的绝对定位。这样,右侧盒子也能无视掉它。
.wrapper-absolute .left {
position: absolute;
}
.wrapper-absolute .right {
margin-left: 150px;
}
缺点:
使用了绝对定位,若是用在某个div中,需要更改父容器的
position
。没有清除浮动的方法,若左侧盒子高于右侧盒子,就会超出父容器的高度。因此只能通过设置父容器的
min-height
来放置这种情况。
使用float+BFC
方法
上面的方法都需要通过左侧盒子的宽度,计算某个值,下面三种方法都是不需要计算的。只需要设置两个盒子之间的间隔。
.wrapper-float-bfc {
overflow: auto;
}
.wrapper-float-bfc .left {
float: left;
margin-right: 20px;
}
.wrapper-float-bfc .right {
margin-left: 0;
overflow: auto;
}
这个方案同样是利用了左侧浮动,但是右侧盒子通过overflow: auto;
形成了BFC,因此右侧盒子不会与浮动的元素重叠。
an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself。
这种情况下,只需要为左侧的浮动盒子设置margin-right
,就可以实现两个盒子的距离了。而右侧盒子是block
级别的,所以宽度能实现自适应。
缺点:
- 父元素需要清除浮动
flex
方案
.wrapper-flex {
display: flex;
align-items: flex-start;
}
.wrapper-flex .left {
flex: 0 0 auto;
}
.wrapper-flex .right {
flex: 1 1 auto;
}
flex
可以说是最好的方案了,代码少,使用简单。有朝一日,大家都改用现代浏览器,就可以使用了。
需要注意的是,flex
容器的一个默认属性值:align-items: stretch;
。这个属性导致了列等高的效果。
为了让两个盒子高度自动,需要设置: align-items: flex-start;
grid
方案
又一个新型的布局方式。可以满足需求,但这并不是它发挥用处的真正地方。
.wrapper-grid {
display: grid;
grid-template-columns: 120px 1fr;
align-items: start;
}
.wrapper-grid .left,
.wrapper-grid .right {
box-sizing: border-box;
}
.wrapper-grid .left {
grid-column: 1;
}
.wrapper-grid .right {
grid-column: 2;
}
注意:
grid
布局也有列等高的默认效果。需要设置:align-items: start;
。grid
布局还有一个值得注意的小地方和flex
不同:在使用margin-left
的时候,grid
布局默认是box-sizing
设置的盒宽度之间的位置。而flex
则是使用两个div的border
或者padding
外侧之间的距离。
极限情况
最后可以再看一下在父容器极限小的情况下,不同方案的表现。主要分成四种情况:
-
动态计算宽度的情况
两种方案: 双inline-block方案和双float方案。宽度极限小时,右侧的div宽度会非常小,由于遵循流动布局,所以右侧div会移动到下一行。
-
动态计算右侧margin-left的情况
两种方案: float+margin-left方案和absolute+margin-left方案。宽度极限小时,由于右侧的div忽略了文档流中左侧div的存在,所以其依旧会存在于这一行,并被隐藏。
-
float+BFC
方案的情况这种情况下,由于BFC与float的特殊关系,右侧div在宽度减小到最小后,也会掉落到下一行。
-
flex
和grid
的情况这种情况下,默认两种布局方式都不会放不下的div移动到下一行。不过 flex布局可以通过 flex-flow: wrap;来设置多余的div移动到下一行。 grid布局暂不支持。
如果感觉写的有问题,恳请指出。
五、圣杯布局
关于「圣杯布局」(转载)
引言
「圣杯布局」—— 尽管这是一个很古老的话题了,而且网上早就有许多相关的文章,但作为前端入门和面试的必备知识之一,还是觉得有必要温故而知新一番。尤其是在拜读了「yuanzm」的博客《我是如何同时拿到阿里和腾讯offer的》文章后,更感到对于任何别人的知识至少都需要花点时间自己实践一下,了解一下它的原理甚至是历史,而不应盲目引用和浮于表面。谨以此文表示感谢~
圣杯的由来
圣杯布局是讨论「三栏液态布局」的实现,它最早出自于谁或许不得而查了,但最早的完美实现是来自于 Matthew Levine 于2006年在「A LIST APART」上写的一篇文章,它主要讲述了网页中关于最佳圣杯的实现方法。
所谓液态布局是相对固态布局而言的,固态布局就是固定值不变的布局,液态就好比在容器里到了一杯水,它可以随着容器宽度的变化而自适应宽度。
在这篇文章中,作者指出了当时的一些实现方式所存在的问题,如:必须按照源顺序(在 DOM 中表现为先写 Left,然后 Middle,最后,Right)等,它将可能导致代码不够灵活,尤其是从 DOM 的载入顺序上来说,中间的内容不能被首先加载。
因此他给出一个方案,它将:
- 两边带有固定宽度中间可以流动(fluid);
- 允许中间一栏最先出现;
- 允许任意一栏放在最上面;
- 仅需一个额外的
div
标签 - 仅需非常简单的 CSS,带上最少的兼容性补丁
文中还提到了他的这个想法是基于「One True Layout」 和 「 Eric Meyer’s adaptation」两篇文章带来的灵感。
在这里你可以看到「圣杯布局」的最终效果:http://alistapart.com/d/holygrail/example_1.html
实现方式
接下来就言归正传,说下具体的实现思路:
首先我们需要布局的 HTML 代码如下,作者在这里为了便于表达,对标签使用了非语义化的 id
,他建议在其他任何正式项目中尽量使用语义化的 id
。例如,我们需要实现的是左侧宽度为 200px,右侧宽度为 150px,中间是流动的布局。
#header
#center
#left
#right
正如他先前提到的,只是加了一层额外的 div
它的 id 是 container
。它的 CSS 内容非常简单,具体代码如下:
body {
min-width: 550px; /* 2x LC width + RC width */
}
#container {
padding-left: 200px; /* LC width */
padding-right: 150px; /* RC width */
}
#container .column {
height: 200px;
position: relative;
float: left;
}
#center {
background-color: #e9e9e9;
width: 100%;
}
#left {
background-color: red;
width: 200px; /* LC width */
right: 200px; /* LC width */
margin-left: -100%;
}
#right {
background-color: blue;
width: 150px; /* RC width */
margin-right: -150px; /* RC width */
}
#footer {
clear: both;
}
#header,
#footer {
background-color: #c9c9c9;
}
/*** IE6 Fix ***/
* html #left {
left: 150px; /* RC width */
}
下面我们来分步观察它的实现逻辑:
第1步 建立框架
先写 header, footer 和 container 三个 div
#header
我们将 container 的内边距设置为左右两边各自的宽度。它看起来就像这样:
第2步 加入三栏
此时我们有了基本框架,可以把三栏塞进去了。
#header
#center
#left
#right
接着我们给每一栏配上合适的宽度,并将它们设为浮动。同时我们需要清除 footer 的上下环境,以免遭跟上面三栏一起浮动。
#container .column {
float: left;
}
#center {
width: 100%;
}
#left {
width: 200px; /* LC width */
}
#right {
width: 150px; /* RC width */
}
#footer {
clear: both;
}
注意这里中间一栏的 100% 宽是基于它的父容器 container 的宽度而言的,由于 container 设置了内边距,因此中间栏看起来就处在了网页的中间,但左右两栏由于排在中间栏的后面,且因为空间不够被挤到了中间栏的下面,如下图所示:
第3步 把左侧栏放上去
中间栏已经就位,剩下的事情就是把左右两栏放上去了,接下来我们先放左侧栏。
为了详述过程,这里将分为两个小步骤。首先,我们先将它的外边距设置为 -100%,这样一来,由于浮动的关系,左侧栏就能上位,与中间栏交叠在一起,并占据了左边。而右侧栏由于左侧栏的上位,自动向前浮动到了原来左侧栏的位置。
接着我们要用到相对定位属性(relative),并设置一个与左侧栏等宽的偏移量:
#container .columns {
float: left;
position: relative;
}
#left {
width: 200px; /* LC width */
margin-left: -100%;
right: 200px; /* LC width */
}
可以看到,它设置的 right
属性就是相对于 container 的右边线向左偏移 200px,如此一来,它就完美地跑到了 container 左内边距的位置,也就是我们希望它呆的地方,如下图所示:
第4步 把右侧栏放上去
最后,我们需要把右侧栏放上去,此时只需利用上面的原理把他放到 container 的右外边距的位置即可,我们需要再一次设置一个负外边距的值,它等于右侧栏的宽度:
#right {
width: 150px; /* RC width */
margin-right: -150px; /* RC width */
}
至此,所有的栏目都就位了~
源码地址
https://jsfiddle.net/DotHide/pg47fucg/1/
参考文章
《In Search of the Holy Grail》 by Matthew Levine
六、双飞翼布局
【布局】聊聊为什么淘宝要提出「双飞翼」布局(转载)
前言
突然有一天,脑之里不知怎地蹦出一个词,「双飞翼」,这是很久以前的淘宝提出的一种三栏布局优化方案,然而,时间久了已经不记得(换句话说是不理解)为啥要提出这个布局了,昨天在 SF 上发起了一个提问,但良久未有人答复,幸得@王能全是谁 提醒,终于回想起「双飞翼」的完整意义了。谨以此文同大家分享这段心路历程。
圣杯 & 双飞翼
说到「双飞翼」就不得不提及「圣杯」,两者均为三栏布局的优化解决方案如下图
常规情况下,我们的布局框架使用以下写法,从上到下,从左到右。
header
main
问题倒是没什么问题,然而,如果我们希望中部 main 部分优先显示的话,是可以做布局优化的。
因为浏览器渲染引擎在构建和渲染渲染树是异步的(谁先构建好谁先显示),那么将
部分提前即可优先渲染。
header
main
于是乎,国外的前辈就提出了「圣杯」布局,目的就是通过 css 的方式配合上面的 DOM 结构,优化 DOM 渲染。
我们来简要地了解一下「圣杯」布局,这不是重点。
圣杯布局
demo :https://jsfiddle.net/zwwill/px57xzp4/
header
main
使用了 relative 相对定位
、float
(需要请浮动,此处使用 overflow:hidden;
方法)和 负值 margin
,将 left 和 right 部分「安装」到 wrapper
的两侧,顾名「圣杯」。具体的思路我就不再做赘述了,网上到处都是解释。
圣杯有问题
当然,正常情况下是没有问题的,但是特殊情况下就会暴露此方案的弊端,如果将浏览器无线变窄,「圣杯」将会「破碎」掉。如图,当 main
部分的宽小于 left
部分时就会发生布局混乱。
于是,淘宝软对针对「圣杯」的缺点做了优化,并提出「双飞翼」布局。
双飞翼布局
demo :https://jsfiddle.net/zwwill/5xjyeu9d/
同样的我们来看简码
header
main
同样使用了 float
和 负值 margin
,不同的是,并没有使用 relative 相对定位
而是增加了 dom 结构,增加了一个层级。确实解决了圣杯布局的缺陷。
为什么要设计「双飞翼」布局
双飞翼布局表面上看是很优秀,但是细细想来,为什么要多加一层 dom 树节点,这岂不是增加了 css 样式规则表和 dom 树合并成布局树的计算量吗?
好像绝对定位也可以解决这个问题
细想想,我们可以使用绝对布局,将左右侧边栏定位到到两侧啊?好像也不会出现圣杯布局的毛病?
demo :https://jsfiddle.net/zwwill/awwkpwbL/
header
main
没有使用 float
(不用请浮动)也没有 负值 margin
,仅仅使用了 absolute 绝对定位
,好像更优秀呢?
但是细细想想,单纯的绝对定位有一个问题,「高度不可控」,我们假设,如果 left
部分的高度高于 main
,是不是 left
没有能力撑起整个 wrapper
?
「四不四」~~!
那么我们再来看看双飞翼和圣杯的情况
都是下图。
「应戳死听」~~!
那这么看来,所有的方案都或多或少存在一些问题。综合来看,不管 left
, main
, right
的大小高低如何,「双飞翼」布局都能正常显示,嗯~~确实很优秀。
锤子和钉子
综上所见,「双飞翼」布局更胜一筹。但是,这是一个「锤子和钉子」的问题,我们应该拿着钉子找锤子,而不是拿着锤子找钉子,因为,当你有了最大的锤子,看到什么都是钉子。
唉~,我又在装逼了。 \( ̄︶ ̄)/
说白了,就是,对症下药,没有最好的方案,只有最适合的。关于三栏布局,我帮大家列出一个对照表,以便大家快速选择。
| 优点 | 缺点
:---------:|:----|:----
圣杯 | 结构简单,无多余 dom 层 | 中间部分宽度小于左侧时布局混乱
绝对定位 | 结构简单,且无需清理浮动 | 两侧高度无法支撑总高度
双飞翼 | 支持各种宽高变化,通用性强 | dom 结构多余层,增加渲染树生成的计算量
以上为个人理解,如有不对或可补充之处,还请指点。
另外关于 CSS 布局方案,和前端性能优化部分,移驾一下文章
多行多列类布局方案总结
前端性能优化总结
转载请标明出处
作者:木羽 zwwill
首发地址:#11
七、总结
关于各种两列和三列布局的代码演示地址:https://fenpho.github.io/baidu-ife/day07-08/layout.html
源码地址:https://github.com/fenpho/baidu-ife/blob/master/day07-08/layout.html