IFE-Task-03:三栏式布局
前端的学习需要更多的实践和思考。百度前端技术学院是一个不错的学习地方,其通过任务驱动前端学习者实践和思考。其官网IFE已经结束报名,不过还是可以自行练习。
对于有过页面时间,但想更深入学习的喔从task3开始学习。task3原地址在这,目标是实现左右两栏宽度固定,中间一栏根据父元素宽度填充满。我尝试使用position和float两种方法来解决问题。
- position属性
CSS的 position 属性设置元素的定位方式,为将要定位的元素定义定位规则。(摘自MDN)
其默认值为static,当元素的position属性不为static就被称之为定位元素。其包括相对定位元素、绝对定位元素、粘性定位元素三种类型。最后的粘性属性sticky还处在实验状态中。其余3种属性值如下:
- relative 为相对定位。relative元素依据自身的原始位置进行相对变化,其原始位置保留不会改变布局。和static一样不会脱离文档普通流(normal-flow)。什么是文档普通流
- absolute 为绝对定位。absolute元素会递归查找该元素的祖先元素,如果最近的祖先元素position设置为relavite/absolute/fixed,则依据该祖先元素进行偏移。如果不存在包含上述属性的祖先元素,该元素的位置则由初始包含块(initial containing block)定位。absolute的元素会脱离普通流。
- fixed 为固定定位。fixed元素通过相对于屏幕视窗的位置来指定元素空间。fixed元素依然会脱离普通流。
说完理论,我们思考barelyfitz上的案例。我们知道absolute会脱离普通流,那在使用中出现案例第五步中的遮盖问题。两个absolute定位的元素#div-1a、#div-1b将普通流中的#div-1c遮盖掉。作者使用float属性来解决这个问题。
- float属性
CSS的float 属性可以使一个元素脱离正常的文档流,然后被安放到它所在容器的的左端或者右端,并且其他的文本和行内元素围绕它安放。(摘自MDN)
float能生成一个文字环绕的效果。在解决元素覆盖的问题时,就可以通过float将#div-1a、#div-1b向左浮动 {float:left;}。除了文字环绕有时会成为问题,float还会由于脱离普通流会导致父元素没有抱住浮动元素。
这就需要需要清除浮动。这大致上有3类方法。
空标记法: 空标记法通过添加一个带clear:both属性的标记来解决问题,比如下列形式。
<br style="clear:both" />
直接添加标记会产生没有语义的标记,并且也不能复用。
overflow属性方法: 直接对父元素设置如下列的css。
.container { overflow: hidden; /* 形成新的块级格式化上下文 */ display: inline-block; /* 触发IE的hasLayout */ display: block; }
该方法通过触发为块级格式化上下文(BFC),BFC是一个独立的布局环境,其中的元素布局是不受外界的影响。这样就能避免文字环绕和包含浮动元素了。除了overflow:hidden可以触发BFC外,还有浮动元素、绝对定位元素(absolute、fixed),和属性display值为inline-blocks、table-cells、table-captions或者属性overflow为hidden、auto、scroll值的元素。
clearfix方法: 在浮动元素的父元素上添加一个clearfix class。
.clearfix::after { content:" "; display:block; clear:both; } .clearfix {*zoom:1;}/*IE/7/6*/
使用伪元素 :after 在元素上下文之后产生新的上下文,属性 clear:both 如同在元素闭合标签前插入 ’
’ 。这就是与任务相关的几个CSS属性知识。
- 实践
由于postion会比较麻烦,我先实现float方法。
由示例图可以看出整个页面包含3栏。 我这里参考涂根华的布局方法,采用先载入左右固定栏的方式。
<div class="float clearfix">
<div class="left">左侧固定栏
<img class="team_logo" src="url" alt="group">
<div class="team_name">标题栏</div>
</div>
<div class="right">右侧固定栏
<img class="member_logo" src="url" alt="member1">
<img class="member_logo" src="url" alt="member2">
<img class="member_logo" src="url" alt="member3">
<img class="member_logo" src="url" alt="member4">
</div>
<div class="main">
<article>
中间文章
</article>
</div>
</div>
首先应题目要求,将所有元素的padding属性设置为20px,并且将margin也统一设置为0px;将img标记的大小都设置为指定80px,这里我依据示例图展示的img内边距padding为0px。
* {
margin: 0px;
padding: 20px;
}
body{
font-family: "微软雅黑";
font-size: 15px;
}
img {
height: 80px;
width: 80px;
padding: 0px;
}
然后将float class元素的背景设置为#eee色和颜色为#999的边框。
.float {
border: 1px solid #999;
background: #eee;
min-width: 600px; /* 极端情况:小尺寸屏幕展开元素 */
}
设置完外部框后,就是left、right、main的三列样式了。首先把宽度、边框和背景色抽离出来。由于left class、right class脱离了普通流。我们需要通过main class的外边距确定main的位置。main与left的空隙(20px) + left的padding和left的witdh就是main的左边距,同样的,main的右边距也需要预留right的大小。最终就能将main填充满中间的空间。并且由于元素默认使用20px的内边距,三列元素的真实width应该设置为目标宽度-20px。这是由于在大部分浏览器中的w3c标准盒子模型的width为内容 content的宽度,IE盒子则有所不同,盒子元素的宽度width为 左右border + 左右padding + content width 。我们可以通过设置属性 box-sizing 为 content-box 来指定默认的CSS 盒模型对元素宽高的计算方式。
.left {
width: 160px;
border: 1px solid #999;
background: #fff;
padding: 0px; /* 依据示意图将padding重设为0px而不是20px */
}
.right {
width: 120px;
border: 1px solid #999;
background: #fff;
padding: 0px; /* 依据示意图 */
}
.main {
border: 1px solid #999;
background: #fff;
margin-left:220px; /* 边距 */
margin-right:140px;
}
接下来就是使用浮动布局来实现任务目标,当然不要忘记上述清除浮动的方法。
.float > .left {
float: left;
}
.float > .right {
float: right;
}
/* 清除浮动 */
.clearfix::after {
content:"";
display:block;
clear:both;
}
.clearfix {
*zoom:1; /* 兼容IE/7/6 */
}
在这里还有一个问题,div是块元素,其会另起一行并占据整行。这样就会以下图的方式展示,但是我们需要div与img同一行。当然我们可以更改为内联标记。
不过,我们还可以通过将图片作左浮动。同时我们将文章的样式加粗和居中。这里还需将宽度width设置为180px,以使得文字在剩余空间内居中。
.team_logo {
float: left; /* 使得img和div并排 */
}
.team_name {
width: 180px;
font-weight: bold;
text-align: center;
padding: 0px;
}
最后的布局图如下,其灰色底框由于清除了浮动会自动包含子元素最高的元素。
在已有样式下修改成使用position方法的布局方案。
<div class="position">
<div class="left">左侧固定栏
<img class="team_logo" src="url" alt="group">
<div class="team_name">标题栏</div>
</div>
<div class="right">右侧固定栏
<img class="member_logo" src="url" alt="member1">
<img class="member_logo" src="url" alt="member2">
<img class="member_logo" src="url" alt="member3">
<img class="member_logo" src="url" alt="member4">
</div>
<div class="main">
<article>
中间文章
</article>
</div>
</div>
如上代码,我使用position class来修饰整个内容框。使用relative来修饰position 类,以便absolute子元素能够定位。position下的left和right都通过父元素偏移到正确的位置。
.position {
position:relative; /* 给absolute定位 */
border: 1px solid #999;
background: #eee;
min-width: 600px; /* 极端小的情况展开元素 */
}
.position > .left {
position: absolute;
top: 20px;
left: 20px;
}
.position > .right {
position: absolute;
top: 20px;
right: 20px;
}
这样会有什么问题呢?问题还比较大。当前无法满足“改变中间一栏的内容长度,以确保在中间一栏较高和右边一栏较高时,父元素的高度始终为子元素中最高的高度。”。因为position设置为absolute的元素会被脱离正常的普通流,父元素就不会被absolute的子元素撑大。在我的页面中,容器的宽度会被main class的撑开。
我使用degzhr的方法,自动获取当前分辨率下子元素的高度,并且更新到父元素上。代码如下,其getClassName函数为获得父元素内的指定子元素数组。
// 获取position类下等高布局元素组和其父元素
var positionElement = document.getElementsByClassName("position")[0];
var leftElement = getClassName(positionElement, "left")[0];
var rightElement = getClassName(positionElement, "right")[0];
var mainElement = getClassName(positionElement, "main")[0];
// 获取等高元素组的元素的高度值
var elementsHeight = [];
elementsHeight.push(leftElement.offsetHeight);
elementsHeight.push(rightElement.offsetHeight);
elementsHeight.push(mainElement.offsetHeight);
// 获取等高元素组中最大高度的元素的高度值
var maxHeight = 0;
for(var i = 0, len = elementsHeight.length; i < len; i++) {
maxHeight = maxHeight > elementsHeight[i] ? maxHeight : elementsHeight[i];
}
// 给每一个等高元素组的定义高度为最大高度maxHeight
console.log(maxHeight);
console.log(elementsHeight);
positionElement.style.height = maxHeight + "px";
这样每次打开页面就会将position的高度设置为子元素高度的最大值。当然这只在每次刷新页面时起效果,我调整页面大小时还是会出现问题的。这又需要通过脚本持续获得页面更新消息。总的来说,在这里使用position方法是不太好的,除非能确认容器的高度就能做一个初始高。这个布局方法既脱离了父元素,但子元素定位又以父元素为基准。这种特性可以用作瀑布流布局。
demo地址。
##References plus: