布局是基于文档流、盒模型的概念,当我们关注实际渲染效果时候,需要注意 盒子模型(客体)、以及盒子模型内部的环境也就是格式化上下文
在普通流中,所有的盒子(内联盒子、块级盒子都各自属于一个自己的格式化上下文,格式化上下文即控制元素如何进行布局)。
格式化上下文:元素的边距、边框和填充如何与同一上下文中的其他块交互
块格式化上下文 block formatting contexts
内联格式化上下文 inline formatting contexts
灵活格式化上下文 flex formatting contexts(flex、grid)
盒模型
CSS3 box-sizing 属性定义了应该如何计算一个元素的总宽度和总高度。
取值:content-box | padding-box | border-box
默认值content-box 标准盒子模型:width 与 height 只包括内容的宽和高,不包括border、padding、margin
.box {width: 350px; border: 10px solid black;}
在浏览器中的渲染的实际宽度(包括边框)将是 370px
所以此时如果两个子元素分父元素的宽度,30%+70%最终大于父元素
怎么解决:calc(30%-超出的宽度)、用负值margin去补足容器缺口 IE盒模型border-box更为稳健,现在最著名的CSS框架几乎都采用了border-box,
width = content + padding + border
display
display 属性可以设置元素的内部和外部显示类型
- 元素的外部显示类型 outer display types 决定元素自己在流式布局中的表现(块级、内联元素、行内块元素、即inline、block、inline-block)
- 元素的内部显示类型 inner display types 控制子元素的布局(例如:flow layout、grid、flex)
.container {
display: [ <display-outside> | <display-inside> ] | <display-listitem> | <display-internal> | <display-box> | <display-legacy> ;
writing-mode: vertical-rl | horizontal-tb;
}
补充display:none
vue 的 if-show 的底层代码就是通过display:none实现
- display :none 元素不会占据它本来应该显示的空间
- visibility : hidden会占据空间 display :none + fixed实现弹窗
<!doctype html>
<head>
<style>
body {
min-height: 200vh;
margin: 0;
}
button {
padding: .5em .7em;
border: 1px solid #8d8d8d;
background-color: white;
font-size: 1em;
}
.top-banner {
padding: 1em 0;
background-color: rgba(0, 0, 0, 0.5);
}
.top-banner-inner {
width: 80%;
max-width: 1000px;
margin: 0 auto;
}
/*弹窗*/
.modal {
display: none;
}
/*弹窗de 用来遮住剩余页面,将用户注意力集中*/
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-body {
position: fixed;
top: 3em;
bottom: 3em;
right: 20%;
left: 20%;
padding: 2em 3em;
background-color: white;
overflow: auto;
}
.modal-close {
cursor: pointer;
}
</style>
</head>
<body>
<header class="top-banner">
<div class="top-banner-inner">
<button id="open">open</button>
</div>
</header>
<div class="modal" id="modal">
<div class="modal-backdrop"></div>
<div class="modal-body">
<button class="modal-close" id="close">close</button>
<h2>弹窗标题</h2>
<p>message</p>
<form>
<p>
<label for="email">Email address:</label>
<input type="text" name="email"/>
</p>
</form>
</div>
</div>
<script type="text/javascript">
const openBtn = document.getElementById('open');
const closeBtn = document.getElementById('close');
const modal = document.getElementById('modal');
openBtn.addEventListener('click', function(event) {
event.preventDefault();//阻止元素发生默认的行为
modal.style.display = 'block';//显示弹窗
});
closeBtn.addEventListener('click', function(event) {
event.preventDefault();
modal.style.display = 'none';
});
</script>
</body>
outer display types
ps另一个思路理解浮动,既能设置宽高,又能跟其他元素处于同一行
块级元素
- 总是从新行开始
- width、height、margin、padding都可以控制
- 可以容纳内联元素和其他块元素
- 相邻两个块级盒子之间的垂直间距会遵循外边距折叠原则被折叠,由max-margin决定
- 每个盒子的左外边缘会与包含块左边缘重合。如果是从右到左的排版顺序,则盒子的右外边缘与包含块右边缘重合
- 宽度默认是父元素内容的100%
html
body
iframe
title
hr
dl、dt、dd
<h1></h1>
...
<h6></h6>
<div></div>
<p></p>
<ul></ul>
<ol></ol>
<li></li>
IE会将main元素渲染成行内元素,而不是块级元素。所以在CSS中我们需要修正
行内元素
- 宽度默认是自己内容的宽度
- 行内元素只能容纳文本或其他行内元素。
- 和相邻行内元素在一行上(父不够子先继续填满,一个元素会被拆成两行)
- width、height无效,单水平方向的padding和margin有边距效果
<span></span>
<u></u>
<ins></ins>
<s></s>
<del></del>
<i></i>
<em></em>
<b></b>
<strong></strong>
<a></a>
行内块元素
- 即不独占一行的块级元素
- 相比行内元素中有几个特殊的标签,可以设置宽高width、height、margin、padding、对齐属性
- 默认宽度就是他本身内容的宽度
- 相邻行内块在一行上,但是之间会有空白间隙(父不够子到下一行)
<img/>
<textarea>...</textarea>
<button>..</button>
<input>
<br>
<label>...</label>
<select>...</select>
<canvas>...</canvas>
<progress>...</progress>
<cite>...</cite>
<code>...</code>
<audio>...</audio>
<video>...</video>
BFC block formatting context
前端中原本定义了box模型,但是元素会影响其他元素的布局。这时候就出现了BFC,元素会脱离常规流 ,创建一个新的BFC形成独立空间,里面的元素和外面的元素不会彼此影响
触发BFC的条件: 意味着使用块布局,修改display
值的计算值
- 根元素或包含根元素的元素:display: flow-root list-item
- 浮动元素 :元素的 float 不是 none
left | right | none | inline-start | inline-end
- 绝对定位元素:元素的 position 为 absolute 、sticky、 fixed
- 行内块元素
- 表格单元格 display:table-* 属性的所有表格单元格
表格单元格table-cell
表格标题table-caption
匿名表格单元格元素
table、table-row、table-row-group、table-header-group、table-footer-group(分别是HTML中table、row、tbody、thead、tfoot的默认属性)
- 元素属性 contain(部分浏览器支持): layout、content、strict
- overflow 值不为 visible 的块元素
- 弹性元素:display为 flex 或 inline-flex元素的直接子元素
- 网格元素:display为 grid 或 inline-grid 元素的直接子元素
- 多列容器:column-count 或 column-width 不为 auto,包括 column-count 为 1
.container {
column-width: 200px;
column-count: 3;
}
元素属性 column-span 设置为 all:使元素横跨所有列
多列布局 - 学习 Web 开发 | MDN (mozilla.org)
使用场景
父元素的高度塌陷
父元素大小受子元素影响,当子元素都设置为浮动,浮动的子元素脱离了文档流,被提起来形成了新的队列,下方普通队列(父元素)无法触及它,检测不到它的存在无法被他撑开,父元素的高度可能会塌陷
方法:
- 对父元素触发BFC
触发了BFC的容器就是页面上一个完全隔离开的容器,容器中的元素绝对不会影响到外面的元素
为了保证上述规则,在计算父元素的高度时必须让浮动的子元素参与进来。
- 浮动布局的核心就是让元素脱离普通流,然后使用width/height,margin/padding将元素定位。
- absolute定位的基准是最近的非static定位父对象,而fixed是相对html根节点的定位。两种定位都会脱离普通流
- 所以父元素为relative,子元素为absolute,也会出现跟浮动一样的问题:父对象高度坍塌,子元素不能撑起父对象。
- 对浮动元素后面的元素 clearfix,使用伪类:before 父元素class属性值含 clearfix
.clearfix:before,.clearfix:after {
content:".";
display:table;
}
.clearfix:after {
clear:both;
}
.clearfix {
*zoom:1;
}
清除浮动的理解:拉回文档流。取消“浮动元素的浮动效果”对其他元素造成的影响。IE⑥⑦不兼容
margin包含塌陷、重叠塌陷
margin在垂直方向取最大值
- 包含塌陷:给容器触发BFC
- 重叠塌陷:用容器包裹元素,给容器触发BFC
CSS3的多列属性
display:grid
display:grid 该元素成为一个网格容器,它的子元素成为网格元素。
所有网格元素必须是网格容器的直接子节点
定义网格轨道
新属性定义网格轨道: fr每一列的权重 支持函数repeat
grid-template-rows: 1fr 1fr;
//等同于
grid-template-rows:repeat(2,1fr );
grid-template-columns: 2fr 1fr 1fr 3fr;
//等同于
grid-template-columns: 2fr repeat(2,1fr ) 3fr;
<!doctype html>
<head>
<style>
:root {
box-sizing: border-box;
}
*,
::before,
::after {
box-sizing: inherit;
}
.grid {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-rows: 1fr 1fr;
grid-gap: 0.5em;
}
.grid > * {
background-color: darkgray;
color: white;
padding: 2em;
border-radius: 0.5em;
}
</style>
</head>
<body>
<div class="grid">
<div class="a">a</div>
<div class="b">b</div>
<div class="c">c</div>
<div class="d">d</div>
<div class="e">e</div>
<div class="f">f</div>
</div>
</body>
隐式网格
上面显式定义了一个网格,在实际DOM中如果元素多于网格数会自动扩充网格,这就是隐式网格
设置隐式网格的默认大小值:
grid-auto-rows: 1fr;
grid-auto-colums: 1fr;
指定元素所占位置
grid-column: 1 / 3;
在前面代码的基础上加入
.a{
grid-column: 1 / 3;
}
页面变化为
补充语法
命名网格线
.container {
display: grid;
grid-template-columns: [left-start] 2fr
[left-end right-start] 1fr
[right-end];
grid-template-rows: repeat(4, [row] auto);
grid-gap: 1.5em;
max-width: 1080px;
margin: 0 auto;
}
header,
nav {
grid-column: left-start / right-end;
grid-row: span 1;
}
.main {
grid-column: left;
grid-row: row 3 / span 2;
}
.sidebar-top {
grid-column: right;
grid-row: 3 / 4;
}
命名网格区域
网格区域必须组成一个矩形 .作为名称 代表空出一个网格
.container {
display: grid;
grid-template-areas: "title title"
"nav ."
"main aside1"
"main aside2";
grid-template-columns: 2fr 1fr;
grid-template-rows: repeat(4, auto);
grid-gap: 1.5em;
max-width: 1080px;
margin: 0 auto;
}
特性查询
CSS查看浏览器支持特性与否 回退样式在特性查询之外
@supports (display: grid) {
.portfolio {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 1fr;
grid-gap: 1em;
grid-auto-flow: dense;
}
}
//抑或是
@supports not(display: grid){}
@supports (display: grid) or (display: table){}
@supports (display: grid) and (display: table){}
设置不同的网格大小
给部分元素占多个网格区域
.portfolio .featured {
grid-row: span 3;
grid-column: span 3;
}
前 后
这种变化是默认 grid-auto-flow:row;
display:flex
Flex 布局教程:语法篇 - 阮一峰的网络日志 (ruanyifeng.com) 传统布局基于盒状模型,依赖 display 属性 + position属性 + float属性。09年,W3C提出Flex布局
利用弹性盒子写一个菜单栏
首先一个简单的例子
/*外部容器设置为弹性*/
.site-nav {
display: flex;
padding: .5em;
background-color: #5f4b44;
list-style-type: none;
border-radius: .2em;
}
.site-nav > li {
margin-top: 0;
}
.site-nav > li > a {
display: block;
padding: .5em 1em;
background-color: #cc6b5a;
color: white;
text-decoration: none;
}
/*所有块的左部外边距*/
.site-nav > li + li {
margin-left: 1.5em;
}
/*设置最后一块的左部外边距为auto来让他填满容器,实现靠右的效果*/
.site-nav > .nav-right {
margin-left: auto;
}
基础知识
display:flex 该元素成为弹性容器,他的子元素成为弹性子元素,flex容器存在方向不同的两条轴,主轴和交叉轴。项目主轴大小:子元素占据主轴方向上的长度。 子元素的float、clear、vertical-align失效。
.box{
display: flex;
}
.box{
display: inline-flex;
}
.box{
display: -webkit-flex; /* Webkit内核的浏览器,必须加上-webkit前缀,Safari */
display: flex;
}
属性
容器属性
flex-flow属性:flex-direction+flex-wrap
默认值:row nowrap
- flex-direction 主轴的方向即项目的排列方向
flex-direction:row;//默认控制子元素从左到右
column、row-reverse、column-reverse
在垂直的弹性盒子里,flex的收缩属性不会起作用
- flex-wrap指定弹性元素是否会在容器内折行显示 默认,项目都排列在轴线上,如果超出一行,该属性定义如何换行
nowrap不换行
wrap换行,下一行在下方
wrap-reverse换行,下一行在上方
启用之后,flex-shrink无效,元素不再收缩,直接折行
justify-content、align-content、align-items
对齐方式会影响间距效果
justify-content 当元素没有填满,控制元素沿主轴方向上的等间距 align-content属性值同justify-content,定义多根交叉轴线的对齐方式,即多行情况下的对齐。
align-items 包含块中在交叉轴方向上的对齐方式。 子元素提供了align-self属性来覆盖align-item属性
子元素属性
- 子元素的float、clear、vertical-align失效。
- 弹性子元素像块级元素一样填充可用宽度,但是弹性子元素不一定填满其弹性容器的宽度
- 弹性子元素高度相等,高度由他们的内容决定。(换一个主轴方向就是等宽列)
- 本质上是因为控制副轴上的属性align-items、align-content的初始值是stretch
- 将弹性子元素的margin设置为auto后,就可以实现子元素相对弹性容器居中。
order属性
定义项目排列顺序: 值越小越前,默认值为0
简写属性flex = (flex-basis, flex-grow, flex-shrink)
- flex-basis
元素大小的基准值
作用于宽度还是高度取决于flex-direction
px、em、百分比
- flex-grow
非负整数,子元素的盒模型占据之后的容器宽度可能会有留白
留白会将增长因子视作权重来分给子元素
0代表不增长
- flex-shrink
子元素的盒模型占据之后的容器宽度可能不足
0代表不缩水
flex实现网页布局
<!doctype html>
<head>
<style>
:root {
box-sizing: border-box;
}
*,
::before,
::after {
box-sizing: inherit;
}
body {
background-color: #709b90;
font-family: Helvetica, Arial, sans-serif;
}
body * + * {
margin-top: 1.5em;
}
.container {
max-width: 1080px;
margin: 0 auto;
}
.site-nav {
display: flex;
padding: .5em;
background-color: #5f4b44;
list-style-type: none;
border-radius: .2em;
}
.site-nav > li {
margin-top: 0;
}
.site-nav > li > a {
display: block;
padding: .5em 1em;
background-color: #cc6b5a;
color: white;
text-decoration: none;
}
.site-nav > li + li {
margin-left: 1.5em;
}
.site-nav > .nav-right {
margin-left: auto;
}
.tile {
padding: 1.5em;
background-color: #fff;
}
.flex {
display: flex;
}
.flex > * + * {
margin-top: 0;
margin-left: 1.5em;
}
.column-main {
flex: 2;
}
.column-sidebar {
flex: 1;
display: flex;
flex-direction: column;
}
.column-sidebar > .tile {
flex: 1;
}
.login-form h3 {
margin: 0;
font-size: .9em;
font-weight: bold;
text-align: right;
text-transform: uppercase;
}
.login-form input:not([type=checkbox]):not([type=radio]) {
display: block;
margin-top: 0;
width: 100%;
}
.login-form button {
margin-top: 1em;
border: 1px solid #cc6b5a;
background-color: white;
padding: .5em 1em;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Ink</h1>
</header>
<nav>
<ul class="site-nav">
<li><a href="/">Home</a></li>
<li><a href="/features">Features</a></li>
<li><a href="/pricing">Pricing</a></li>
<li><a href="/support">Support</a></li>
<li class="nav-right">
<a href="/about">About</a>
</li>
</ul>
</nav>
<main class="flex">
<div class="column-main tile">
<h1>Team collaboration done right</h1>
<p>Thousands of teams from all over the
world turn to <b>Ink</b> to communicate
and get things done.</p>
</div>
<div class="column-sidebar">
<div class="tile">
<form class="login-form">
<h3>Login</h3>
<p>
<label for="username">Username</label>
<input id="username" type="text"
name="username"/>
</p>
<p>
<label for="password">Password</label>
<input id="password" type="password"
name="password"/>
</p>
<button type="submit">Login</button>
</form>
</div>
<div class="tile centered">
<small>Starting at</small>
<div class="cost">
<span class="cost-currency">$</span>
<span class="cost-dollars">20</span>
<span class="cost-cents">.00</span>
</div>
<a class="cta-button" href="/pricing">
Sign up
</a>
</div>
</div>
</main>
</div>
</body>