CSS语法和文档流
CSS的厉害之处
CSS的厉害之处在于层叠样式表,其中包括
- 样式层叠:可以多次对同一选择器选择样式声明
- 选择器层叠:可以用不同选择器对同一个元素声明样式
- 文件层叠:可以用多个文件层叠
CSS的版本
CSS2是最广泛使用的版本,兼容一切IE, CSS3是现代使用的版本,之后就分模块升级
caniuse.com查询兼容性
可以在caniuse 上查询CSS样式的在不同浏览器的兼容性
CSS语法
语法一:
选择器 {
属性名:属性值;
/*注释*/
}
例子:
p {
color: red;
}
注意:
- 所有符号都是英语符号
- 区分大小写
- 任何地方写错了,浏览器不会报错,会直接忽略
- 需要写分号
语法二:@语法
@charset "UTF-8"; /*必须放在第一行, 而且要加分号*/
@import url(2.css);
@media (min-width: 100px) and (max-width: 200px) {
语法一
}
CSS的文档流
文档流
文档流是指HTML元素的流动方向
- inline 元素从左到右,到最右边会换行,如果最右边长度不够一个Inline元素,会自动切割为两半,一半在这一行,一半在下一行, 即会跨越两行
- Block元素的流动方向是从上到下,每一个都另起一行
- inline-block元素从左到右,但不会跨越两行
宽度
-
inline元素的宽度是内部inline元素宽度的和,不能用width指定
-
block元素默认自动计算宽度(width: auto),即能占多宽就占多宽,可用width指定
- block元素的默认宽度不是100%,不是100%, 不是100%
- 永远不要对Block元素写 width: 100%;
- inline-block默认宽度是内部inline元素宽度之和,可以用width指定宽度
inline元素是尽可能窄,block元素是尽可能宽
高度
- inline元素的高度是由line-height间接确定(大部分情况下,Inline元素和Line-height一样,但如果字体不一样,会有细微的区别,比如差1-2px),和height无关
- block元素的高度是由内部文档流元素决定的,可以设Height
- inline-block元素和Block元素一样
一个空的div的高度是0
一个空的span元素的高度不是0,因为span元素的高度是line-height决定的
问题是,如果我设置的高度小于内容元素的高度怎么办?
这就是溢出
溢出
- overflow: hidden: 隐藏溢出内容
- overflow: scroll: 显示滚动条,但问题是,如果内容宽度和高度没有溢出,也会有滚动条
- Overflow: auto: 灵活地显示滚动条,只有在需要的时候才会出现滚动条
- Overflow: visible: 直接显示溢出部分 overflow可以分为overflow-x和overflowy
脱离文档流
block元素的高度是由内部文档流元素决定的,这就意味着,如果元素脱离了文档流,block元素就不会计算它的高度。
有两种方式可以脱离文档流:
- position: absolute
- float: left
CSS盒模型
两种盒模型
- content-box 内容盒: 宽度只是内容的宽度
- border-box 边框盒: 宽度包括内容+ padding + border的宽度 举例来说,如果写以下代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
.content-box {
margin:25px;
border: 5px solid red;
padding: 10px;
box-sizing: content-box;
width: 100px;
}
.border-box {
margin:25px;
border: 5px solid red;
padding: 10px;
box-sizing: border-box;
width:100px;
}
</style>
</head>
<body>
<div class="content-box">
内容盒
</div>
<div class="border-box">
边框盒
</div>
</body>
</html>
虽然都设置了相同的宽度为100Px,但可以看到两种盒模型的宽度是不同的 内容盒的盒模型:只有内容宽度是100px,
边框盒的盒模型:padding + border + content = 100px
Margin合并
兄弟合并
第一个div的Margin-bottom (下外边距)会和第二个div的margin-top(上外边距)合并
父子合并
parent的上边距会和第一个孩子的上边距重合,parent的下边距会和最后一个孩子的下边距重合
这种margin合并只存在与上下外边距,左右外边距不会合并
取消Margin 合并
对于兄弟合并:用display:inline-block
对于父子合并:
- 第一种方法是给parent加Border
- 第二种方法是给parent加Paddingmargin能合并就是因为父子的Margin之间没有其他的东西,如果加了border或者Padding就等于在父子的Margin中间加了东西,因此就无法合并margin了
- 第三种方式是给parent加overflow:hidden
基本单位
长度单位
px
em 相对自身font-size的倍数
可以尝试画一个彩虹来练习
示范链接
js.jirengu.com/feladihete/…
总结:
- 用overflow:hidden解决margin合并
- parent div 表示parent下的所有div
- parent>div parent下的第一个div
CSS布局
float布局
步骤:
- 子元素上加 float:left, 和width
- 父元素加上 .clearfix
- 父元素加上class: class="clearfix"
- .clearfix的写法:
.clearfix::after {
content:'';
display:block;
clear:both;
}
一些经验:
- 最后一个子元素不设置width, 或者设置max-width
- img 设置Max-width
- 如果图片下面有多余的空白,就在图片上写上 vertical-align: top或者middle
- 如果border干扰了布局,可以用outline: 1px solid red, outline不占用位置
- 固定元素的块级元素居中的方法: 写上margin: 0 auto; 或者margin-left: auto; margin-right: auto;(比前者好,因为没有影响上下的Margin)
- 平均布局的关键点:负Margin float布局需要手动计算宽度,比较麻烦,但是用flex布局就还好
flex布局
align-items: stretch;
即使内容高度不一样,也让item的高度一样
item的属性:
- order: 默认是0
按照order从小到大的顺序排
负数、0、正数 - flex-grow
使用技巧:三栏布局时,两边的不设置,中间的设置flex-grow:1.
实战分析:
想让导航栏去到右边有两种写法
- 在父元素上设置justify-content:space-between;
- 或者在导航栏上写margin-left: auto 在写平均布局时,即使用Justify-content:space-between也无法满足要求:
第一行是对的,但是第二行就错了,所以我们还是需要用到负margin
首先在Image上加上一个wrapper div
然后再加上负Margin,负Margin的值就是每个Image的Margin-right的值
.imageList > .wrapper {
display: flex;
flex-wrap: wrap;
margin-right: -12px;
}
预览地址:
练习flex布局的小游戏:flexboxfroggy.com/#zh-cn
grid布局
grid-template-columns: 40px 50px auto 50px 40px;
grid-template-rows: 60px 400px 200px;
表示5列3行
用法展示:
js.jirengu.com/yuwifamowe/…
fr的用法: js.jirengu.com/zokevujila/…
用fr来实现平均布局,就不在需要写负Margin了
js.jirengu.com/somodiyoda/…
grid用法总结:
- 用grid-template-areas 设计大致的布局
示例:
grid-template-areas:
"big mid1"
"big mid2"
"sm1 mid2"
"sm2 mid3"
"sm3 mid3"
- 用grid-template-rows和grid-template-columns指定每一行每一列的高度和宽度 示例:
grid-template-rows: 240px repeat(4, 120px);
grid-template-columns: 250px 250px;
- 对于每一个小块,用grid-area和grid-template-areas 里布局的字符串对应 示例:
.demo > .image:first-child{
grid-area: big;
border: 1px solid red;
}
grid尤其适合用来做不规则布局
假设我们想做以下布局:
代码示例:
CSS元素居中方法
绝对元素的居中定位写法:
#demo {
position: absolute;
left:50%;
top: 50%;
transform: translate(-50%, -50%);
}
grid布局练习游戏: cssgridgarden.com/#zh-cn
CSS动画
如果我们想要实现一个div向左移动的动画,应该怎么做呢?
有三种实现方式,接下来首先分别介绍三种方式,之后会根据浏览器渲染原理分析哪种方式最好
1. 通过设定left
实现样例点击:js.jirengu.com/bagow/1/edi…
核心的JS代码是:
var n = 1
var id = setInterval(() => {
console.log(n)
if (n <= 50) {
demo.style.left = n / 100 * 300 + 'px'
n = n + 1
}else{
clearInterval(id)
}
}, 1000 / 30)
关键就是通过改变left实现div右移
2. 通过用transform + transition
实现思路:使用transform属性里的translateX来完成移动,然后加上transition的过渡效果。
实现案例:
js.jirengu.com/qotexonazi/…
核心代码:
CSS代码:
#demo {
width: 50px;
height: 50px;
background-color: red;
/*给demo加一个简单的样式以方便查看*/
transition: all 1s;
}
#demo.go{
transform:translateX(100px);
/*这里提前写一个类go,用translateX来完成向右移动*/
}
CSS代码有两行重点:
- transition: all 1s; 让整个移动在1s内完成,
- transition的作用就是给我开头和结尾,我来补充中间帧
- transform:translateX(100px); 用translateX(100px)来实现右移 JS代码:
start.onclick = function(){
demo.classList.add('go');
//给按钮添加点击事件,当点击按钮的时候,就给demo加上go类
}
实现效果:
3. 用animation
animation的核心就是定义关键帧, 其中go是这个动画的名字
1.第一种写法,用from, to
@keyframes go {
from {transform:none;}
to {transform:translateX(100px);}
}
2.第二种写法
@keyframes go {
0 {transform:none;}
100% {transform:translateX(100px);}
}
然后,多写一个start类,指定让go运行2秒
#demo.start{
animation:go 2s;
}
JS添加上这个类:
start.onclick = function(){
demo.classList.add('start');
//给按钮添加点击事件,当点击按钮的时候,就给demo加上go类
}
预览效果:
关于transition的总结
transition就是过渡的意思,作用就是我一个行为开头和结尾,然后中间我来帮你添加一个过渡的效果。
语法:
/* Apply to 1 property */
/* property name | duration */
transition: margin-right 4s;
/* property name | duration | timing function */
transition: margin-right 4s ease-in-out;
/* property name | duration | timing function | delay */
transition: margin-right 4s ease-in-out 1s;
/* Apply to 2 properties */ 用逗号隔开
transition: margin-right 4s, color 1s;
/* Apply to all changed properties */ 用all表示给所有Property添加动画
transition: all 0.5s ease-out;
有一些属性不能用transition:
比如display:block变成display:none
js.jirengu.com/molonucuxu/…
当我们想做的动画效果是让一个显示的div消失,可以有两种方法:
- 用Opacity最开始设置opacity:1, hover后设置opacity:0 但是Opacity为0后,这个元素仍然占有相应空间,可以用Js去掉这个元素可以用的JS事件是xxxx.ontransitionend
demo.addEventListener('transitionend', function(){
demo.remove()
})
- 还可以改为用visibility: visible 改成hidden 总而言之:可以用opacity : 1 -> 0 或者 visibility: visible -> hidden 替代 display: block -> none
关于animation的总结
animation组成部分
- 关键帧 keyframes
- animation 属性 因为我们可以在任意一个点指定关键帧,所以animation可以用来做更复杂的动画
稍微复杂一些的动画
假设要做一个让div先向左平移150像素,再向下平移150像素,动画延迟1秒后开始,并且在2秒内完成,动画完成后还要一直自动的重复这样的动画:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
<style>
#demo {
width: 50px;
height: 50px;
background-color: red;
animation: go 2s linear 1s infinite alternate;
}
@keyframes go {
0%{
transform:none;
}
50%{
transform:translateX(150px);
}
100%{
transform:translateX(150px) translateY(150px);
}
}
</style>
</head>
<body>
<div id="demo"></div>
<button id="start">开始</button>
</body>
</html>
CSS代码中的关键就是:
- 定义关键帧:
@keyframes go {
0%{
transform:none;
}
50%{
transform:translateX(150px);
}
100%{
transform:translateX(150px) translateY(150px);
}
}
- animation属性
#demo {
....
animation: go 2s linear 1s infinite alternate;
}
其中infinite 表示无限运动; alternate 表示来回运动
如果想添加一个暂停和继续的功能,怎么实现呢?
实现思路:animation每个属性都有对应的属性名,比如控制动画暂停和继续的属性名是animation-play-state,知道属性名和属性值后,就可以通过修改属性值来实现功能:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
<style>
#demo {
width: 50px;
height: 50px;
background-color: red;
}
#demo.begin {
animation: go 2s linear 1s infinite alternate;
}
@keyframes go {
0% {
transform: none;
}
50% {
transform: translateX(150px);
}
100% {
transform: translateX(150px) translateY(150px);
}
}
</style>
</head>
<body>
<div id="demo"></div>
<br>
<button id="start">开始</button>
<button id="pause">暂停</button>
<button id="goOn">继续</button>
<script>
start.onclick = function () {
demo.classList.add('begin');
}
pause.onclick = function () {
demo.style.animationPlayState = 'paused';
}
goOn.onclick = function () {
demo.style.animationPlayState = 'running';
}
//注意:animation-play-state 和 animationPlayState的书写方式的区别
</script>
</body>
</html>
实现效果如下:
浏览器渲染原理
通过分析浏览器渲染原理,分析animation和transform+transition比直接改Left要好
浏览器渲染的过程:
- 根据HTML标记并构建DOM树
- 根据CSS构建CSS树(CSSOM)
- 将两棵树合并成一棵渲染树(render tree
- layout布局(文档流,盒模型,计算大小或位置等)
- paint绘制(边框颜色,背景颜色,阴影等绘制)
- compose合成(根据层叠关系展示画面)
更新样式的三种方式
- JS / CSS > 样式 > 布局 > 绘制 > 合成
根据浏览器的渲染原理,若是开发者更新了样式(即元素的几何属性,类似于宽高,位置等),则浏览器会检查所有属性然后重新绘制,最后再合成。之前用到的改变left法,就会每次都让浏览器重新布局、渲染和合成
- JS / CSS > 样式 > 绘制 > 合成
如果开发者只是更新了paint only的属性(例如背景,文字颜色等),由于不影响页面布局,则浏览器直接执行绘制。
- JS / CSS > 样式 > 合成
在开发者只是更改一个既不更改布局也不需要绘制的属性时,浏览器将只执行合成,例如动画(animation)和transform。
可以用以下网址查询不同的CSS属性会触发的不同过程:
本文为fjl的原创文章,著作权归本人和饥人谷所有,转载务必注明来源