《CSS 知识总结》

361 阅读10分钟

CSS语法和文档流

CSS的厉害之处

CSS的厉害之处在于层叠样式表,其中包括

  • 样式层叠:可以多次对同一选择器选择样式声明
  • 选择器层叠:可以用不同选择器对同一个元素声明样式
  • 文件层叠:可以用多个文件层叠

CSS的版本

CSS2是最广泛使用的版本,兼容一切IE, CSS3是现代使用的版本,之后就分模块升级

caniuse.com查询兼容性

可以在caniuse 上查询CSS样式的在不同浏览器的兼容性

CSS语法

语法一:

选择器 {
属性名:属性值;
/*注释*/
}

例子:

p {
 color: red;
}

注意:

  1. 所有符号都是英语符号
  2. 区分大小写
  3. 任何地方写错了,浏览器不会报错,会直接忽略
  4. 需要写分号

语法二:@语法

@charset "UTF-8";  /*必须放在第一行, 而且要加分号*/
@import url(2.css);
@media (min-width: 100px) and (max-width: 200px) {
	语法一
}

CSS的文档流

文档流

文档流是指HTML元素的流动方向

  • inline 元素从左到右,到最右边会换行,如果最右边长度不够一个Inline元素,会自动切割为两半,一半在这一行,一半在下一行, 即会跨越两行
  • Block元素的流动方向是从上到下,每一个都另起一行
  • inline-block元素从左到右,但不会跨越两行

1592202329345-101567d0-c248-42b8-b9bb-5d4090a8f996.png

宽度

  • inline元素的宽度是内部inline元素宽度的和,不能用width指定

  • block元素默认自动计算宽度(width: auto),即能占多宽就占多宽,可用width指定

  1. block元素的默认宽度不是100%,不是100%, 不是100%
  2. 永远不要对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元素就不会计算它的高度。
有两种方式可以脱离文档流:

  1. position: absolute
  2. float: left

CSS盒模型

1592204847319-775912da-464a-4715-b753-fb2e6d1d810a.png

两种盒模型

  1. content-box 内容盒: 宽度只是内容的宽度
  2. 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>

1592212706228-abaf7631-3bd8-457b-bf97-17be183ba7c1.png

虽然都设置了相同的宽度为100Px,但可以看到两种盒模型的宽度是不同的 内容盒的盒模型:只有内容宽度是100px,

1592212723006-1cc0c0af-8198-42d0-8290-cc64a48ade58.png

边框盒的盒模型:padding + border + content = 100px

1592212762117-8833caca-0f47-408e-83b7-9413f9652519.png

Margin合并

兄弟合并

1592213454064-e0d10a96-e41e-41fa-bdb7-8af3391c0844.png 第一个div的Margin-bottom (下外边距)会和第二个div的margin-top(上外边距)合并

父子合并

1592214621126-e763b999-cc39-47d8-b150-aaee90e15083.png 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
  1. 父元素加上class: class="clearfix"
  2. .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的高度一样

1592293263316-5c14fac1-d300-4f27-962f-ec54aeefef81.png

item的属性:

  • order: 默认是0 按照order从小到大的顺序排
    负数、0、正数
  • flex-grow 使用技巧:三栏布局时,两边的不设置,中间的设置flex-grow:1.

实战分析:

1592296037826-dfd809e7-b72c-4db2-b6a0-9ebe0a221c8e.png

想让导航栏去到右边有两种写法

  • 在父元素上设置justify-content:space-between;
  • 或者在导航栏上写margin-left: auto 在写平均布局时,即使用Justify-content:space-between也无法满足要求:

1592296754051-73aa20e1-4926-420c-9818-2cb559ae403c.png 第一行是对的,但是第二行就错了,所以我们还是需要用到负margin
首先在Image上加上一个wrapper div

1592296961191-12b63cb1-a2df-478f-9110-2cad8e3f359d.png

然后再加上负Margin,负Margin的值就是每个Image的Margin-right的值

.imageList > .wrapper {
  display: flex;
  flex-wrap: wrap;
  margin-right: -12px;
}
预览地址:

js.jirengu.com/hiwalumapu/…

练习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用法总结:
  1. 用grid-template-areas 设计大致的布局
    示例:
 grid-template-areas:
    "big mid1"
    "big mid2"
    "sm1 mid2"
    "sm2 mid3"
    "sm3 mid3"
  1. 用grid-template-rows和grid-template-columns指定每一行每一列的高度和宽度 示例:
grid-template-rows: 240px repeat(4, 120px);
grid-template-columns: 250px 250px;
  1. 对于每一个小块,用grid-area和grid-template-areas 里布局的字符串对应 示例:
.demo > .image:first-child{
  grid-area: big;
  border: 1px solid red;
}
grid尤其适合用来做不规则布局

假设我们想做以下布局:

1592386399540-98a24c5b-e892-4258-b81b-4e1cc90f2a6d.png

代码示例:

js.jirengu.com/qififesuga/…

CSS元素居中方法

绝对元素的居中定位写法:

#demo {
 position: absolute;
  left:50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
grid布局练习游戏: cssgridgarden.com/#zh-cn

CSS动画

如果我们想要实现一个div向左移动的动画,应该怎么做呢?

1592898225543-2bf105de-71cc-49d5-adf8-a44e3544ad51.gif

有三种实现方式,接下来首先分别介绍三种方式,之后会根据浏览器渲染原理分析哪种方式最好

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类
}

实现效果:

1592898715775-81d4f748-3e91-4ac6-8c62-7efd1bef7c6f.gif

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类
}

预览效果:

js.jirengu.com/zofaxafiba/…

关于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消失,可以有两种方法:

  1. 用Opacity最开始设置opacity:1, hover后设置opacity:0 但是Opacity为0后,这个元素仍然占有相应空间,可以用Js去掉这个元素可以用的JS事件是xxxx.ontransitionend
demo.addEventListener('transitionend', function(){
  demo.remove()
})
  1. 还可以改为用visibility: visible 改成hidden 总而言之:可以用opacity : 1 -> 0 或者 visibility: visible -> hidden 替代 display: block -> none
关于animation的总结

animation组成部分

  • 关键帧 keyframes
  • animation 属性 因为我们可以在任意一个点指定关键帧,所以animation可以用来做更复杂的动画
稍微复杂一些的动画

假设要做一个让div先向左平移150像素,再向下平移150像素,动画延迟1秒后开始,并且在2秒内完成,动画完成后还要一直自动的重复这样的动画:

1592899969497-ff64fce9-b575-4594-ae28-2b824dda7a40.gif

<!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>
实现效果如下:

1592900181536-825b9d66-b81f-459b-9c5c-ea0ebe9825cf.gif

浏览器渲染原理

通过分析浏览器渲染原理,分析animation和transform+transition比直接改Left要好

浏览器渲染的过程:

  1. 根据HTML标记并构建DOM树
  2. 根据CSS构建CSS树(CSSOM)
  3. 将两棵树合并成一棵渲染树(render tree
  4. layout布局(文档流,盒模型,计算大小或位置等)
  5. paint绘制(边框颜色,背景颜色,阴影等绘制)
  6. compose合成(根据层叠关系展示画面)

1592900331664-b5e8463d-13d1-481e-bedc-8a5b01b69e1d.png

更新样式的三种方式

  1. JS / CSS > 样式 > 布局 > 绘制 > 合成

1592900355181-4507c517-2b45-46cd-b78c-72343610112f.webp 根据浏览器的渲染原理,若是开发者更新了样式(即元素的几何属性,类似于宽高,位置等),则浏览器会检查所有属性然后重新绘制,最后再合成。之前用到的改变left法,就会每次都让浏览器重新布局、渲染和合成

  1. JS / CSS > 样式 > 绘制 > 合成

1592900354969-aa6209fe-6262-4f68-96a9-9339305dafdb.webp

如果开发者只是更新了paint only的属性(例如背景,文字颜色等),由于不影响页面布局,则浏览器直接执行绘制。

  1. JS / CSS > 样式 > 合成

1592900354998-30a47939-d089-4eaf-ad92-ee12a6ad9f97.webp

在开发者只是更改一个既不更改布局也不需要绘制的属性时,浏览器将只执行合成,例如动画(animation)和transform。

可以用以下网址查询不同的CSS属性会触发的不同过程:

csstriggers.com/

本文为fjl的原创文章,著作权归本人和饥人谷所有,转载务必注明来源