前端知识体系(2) —— CSS 基础(1)

695 阅读32分钟

前言:本前端知识体系供个人复习与巩固用,不一定适合其他人。会逐步的完善该系列文章。

CSS基础概念

名称

Cascading style sheets (层叠样式表)

语法结构

编写的时候要注意空格换行以及声明后的分号

CSS 注释

其语法如下:

/* 这是一行单行注释 */

/*
这个注释分散
在多个行上面
*/

几个例子:

辅助阅读注释

/* logo 样式 */
.logo {
 width: 200px; /* 宽度200px */
}

单行代码注释

/* logo 样式 */
.logo {
 /* width: 200px; */
}

多行代码注释

/* logo 样式 */
.logo {
    width: 200px;
    /* height: 100px;
    position: relative;
    left: 10px;
    top: 40px; */
}

HTML 中引入 CSS 的方式

行内方式

通过给元素添加 style 属性来添加样式,如下:

<div style="color: red;">颜色为红色</div>
<div style="color: red;">颜色为红色</div>

它有一个很明显的缺点:只能改变当前标签的样式,如果想要多个元素都使用同一种样式,每个元素都要添加一遍,这样不仅导致HTML代码冗长难以阅读,而且后期维护起来也非常艰难。

所以一般不会大量使用这种方式添加样式,只有在少量使用 js 改变样式的时候使用。

内嵌方式

通过在<head>元素中使用<style>元素来定义:

<head>
    <style type="text/css">
        div {
            color: red;
        }
    </style>
</head>

该方法有效解决了第一个行内方式的带来的多元素不能重用同一个样式的问题,但是同时又暴露出了另一个问题:没有办法和其他页面共用,只能本页面使用。所以当多个页面需要引入相同的 CSS 代码时,这样写会导致代码冗余,也不利于维护。

这种方式一般在写简单 demo 的时候或者在移动端追求性能优化时使用。

外链方式

通过在<head>元素中使用<link>元素来引入:

<head>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

这是网页最常见的也是最推荐的引入 CSS 的方式,具备良好的可维护性。并且如果多个页面共用一个 CSS 文件的话,一般只在第一次加载时需要下载,以后切换页面时根本不需要加载该文件(浏览器一般有缓存)。

导入方式

其实除了上面三种方式之外,还有第四种方法,就是@import导入的方式。这种方式呢建议只作为一个了解,建议不要使用。

引入方式如下:

<style>
    @import url(style.css);
</style>

可以看到这个这个方式也是引用一个外部CSS文件,那么和上面的外链方式有什么区别呢?

  • 范畴不同: <link> 属于 HTML 元素,通过其href属性来引入外部文件;而 @import 属于 CSS,所以导入语句应写在 CSS 中,要注意的是导入语句应写在样式表的开头,否则无法正确导入外部文件
  • 兼容性差别: @import 是 CSS2.1 才出现的概念,所以如果浏览器版本较低,无法正确导入外部样式文件;而<link>则没有任何兼容问题;
  • 加载顺序不同:当 HTML 文件被加载时,<link>引用的文件会同时被加载,而 @import引用的文件则会等页面全部下载完毕再被加载
  • js 修改支持:<link>支持使用JavaScript控制DOM改变CSS样式,@import不支持

总结

在实际网页中应尽量使用 <link> 标签导入外部 CSS 文件,避免或者少使用其他三种方式。

浏览器调试工具打开方式

  1. 快捷键
    • windows: F12
    • mac: cmd + option + i
  2. 右键菜单
  3. 浏览器入口

选择器

选中需要添加样式的元素

基本选择器

元素选择器

p {
    color: red;
}

ID选择器

  • 取值全局唯一
#green {
    color: green;
}

类选择器

  • 可应用于多个元素
  • 可组合使用
.fs14 {
    font-size: 14px;
}

通用选择器

  • 通配符,可以选中所有的 HTML 元素
  • 可以理解为特殊的元素选择器
* {
    margin: 0;
    padding: 0
}

选择器分组

简化代码,共用代码。

关系选择器

后代选择器

选择所有代

.block p {
    color: green;
}

子元素选择器

只选择子元素的那一代

.block > p {
    color: blue;
}

兄弟关系选择器

后面的兄弟

p ~ div {
    width: 200px;
}

紧跟其后的一个兄弟

p + div {
    width: 300px;
}

伪类选择器

状态类型

  • :link 设置 a 元素在未被访问前的 CSS 样式
  • :visited 设置 a 元素在其链接地址已被访问过时的 CSS 样式
  • :hover 设置元素在其鼠标悬停时的 CSS 样式
  • :active 设置元素在被用户激活(在鼠标点击与释放之间的事件)时的 CSS 样式

结构类型

  • :first-child 第一个元素
  • :last-child 最后一个元素
  • :nth-child(n)(正着数) :nth-last-child(n)(倒着数) n 可以是数字(4),关键词(odd 奇数, even 偶数)或公式(2n + 1)

伪元素选择器

  • ::first-letter 第一个字
  • ::first-line 第一行
  • ::before 内容之前插入一个元素
  • ::after 内容之后插入一个元素

属性选择器

可以用一些属性来表示元素的一些特征。利用这些特征来选中该元素,如类选择器及 id 选择器,其实都是利用属性 class 和 id 的值构造起来的选择器,这可以视作 class 和 id 属性的一种特殊待遇,而其他的属性就得按部就班来了。

总的来说,属性选择器搭配比较自由,既可以根据属性来选,也可以根据属性值来选,还可以根据部分属性值来选,具体规则如下:

选择器描述
[attribute]用于选取带有指定属性的元素
[attribute=value]用于选取带有指定属性和值的元素
[attribute^=value]匹配属性值以指定值开头的每个元素
[attribute$=value]匹配属性值以指定值结尾的每个元素
[attribute*=value]匹配属性值中包含指定值的每个元素
[attribute~=value]用于选取属性值中包含指定词汇的元素

下面使用这四个 a 元素来具体实践下:

<a href="https://www.baidu.com" target="_blank">百度</a>
<a href="css-basic.pdf" >CSS学习文档</a>
<a href="css.png" >CSS 脑图</a>
<a href="http://www.google.com" title="google">谷歌</a>

要求如下:

  • 选中 title 属性链接
  • 选中新窗口打开的链接(可在后面添加一个icon,以区分其他链接)
  • 选中不同的文件类型链接(可在后面添加对应的图标,以表示资源类型)
  • 选中绝对路径链接

对应的选择器如下:

/* 选中 title 属性链接 */
a[title] {}

/* 选中新窗口打开的链接 */
a[target="_blank"] {}

/* 选中 pdf */
a[href$=".pdf"] {}

/* 选中 png */
a[href$=".png"] {}

/* 选中绝对路径链接 */
a[href^="http://"],
a[href^="https://"] {}

选择器优化

对于浏览器来说,解析每种选择器所耗费的时间并不是一样的。所以当使用选择器的时候也有必要了解如何才能写出最优选择器。

选择器效率

根据网站效率专家 Steve Souders 指出,各种 CSS 选择器的效率由高至低排序如下:

  • id选择器(#myid)
  • 类选择器(.myclassname)
  • 标签选择器(div,h1,p)
  • 相邻选择器(h1 + p)
  • 子选择器(ul > li)
  • 后代选择器(li a)
  • 通配符选择器(*)
  • 属性选择器(a[rel="external"])
  • 伪类选择器(a:hover,li:nth-child)

以下面的 p 元素为例:

<p id="test" class="red">我用来测试选择器的优化</p>

可以通过很多种方法去选中它,如p,.red,#test,[class="red"]等等。但是如果按照执行效率来说,id选择器是最佳的,其次是类选择器,然后是元素选择,最后才是属性选择器。

选择器解读顺序

一般来说,在具体的项目中,HTML 结构都比较复杂,所以关系选择器使用非常的普遍。对于关系选择器来说,阅读习惯是从左到右,但是浏览器解读选择器,遵循的原则是从选择器的右边到左边读取。

如对于选择器.list .item .item-tt,浏览器先找的是.item-tt,然后继续向父级元素寻找.item,再找.list,这样才完成了最终的选择器匹配。

所以如果路径链越短,效率也就相应有所提高。这里建议选择器的层级最多不要超过4层,如.demo .list .item .item-tt .tt-link就有5层了,可根据实际情况考虑缩短为4层以内,如.demo .item-tt .tt-link

参考资料

选择器参考手册

首先是 W3school 的选择器参考,归类很详细:

或者直接参考选择器手册:

属性

参考文档:

字体相关属性

  • font-family:定义文本的字体,如:font-family: arial;
  • font-size:字体尺寸,如:font-size: 18px;
  • font-style :字体样式,如:font-style: italic;
  • font-weight:字体的粗细,如:font-weight: bold;

文本相关属性

  • color:定义文字颜色,如:color: red;
  • line-height:设置行高,如:line-height: 1.5;
  • text-align:文本的水平对齐方式,如:text-aligin: center;
  • text-decoration:文本的装饰效果,如:text-decoration: underline;
  • text-indent:首行的缩进,如:text-indent: 2em;
  • text-shadow:文本的阴影效果,如:text-shadow: 0 0 5px #ff0000;

列表属性

  • list-style:在一个声明中设置所有的列表属性
  • list-style-image:将图象设置为列表项标记
  • list-style-position:设置列表项标记的放置位置
  • list-style-type:设置列表项标记的类型

上面的几个属性一般只作用于ul/ol、li元素重置的时候(其他时候几乎从来不用),使用的时候也是使用第一个简写的形式,如:

ul, ol {
    /* 第一个none表示image,outside表示position,第二个none表示type */  
    list-style: none outside none; 
}

表格属性

  • border-collapse:是否合并表格边框
  • border-spacing:相邻单元格边框之间的距离
  • table-layout:设置表格的布局算法

上面三个属性,只作用于 table 元素,其余元素都没有作用,如:

table {
    border-collapse:collapse;
    border-spacing: 0;
    table-layout: fixed;
}

盒子相关

盒子大小

主要是宽高及最小和最大宽高。

  • width
  • min-width
  • max-width
  • height
  • min-height
  • max-height
  • box-sizing

简单示例如下:

div {
    width: 200px;
    min-height: 400px;
}

盒子边框

每个元素都有四条边,可以给任何一条边设置边框,分别为上(top)、右(right)、下(bottom)、左(left)表示,而每个边框又包括宽度(width)、样式(style)及颜色(color)三个样式,这样组合起来就有很多属性了,不过一般使用简写的模式来写。

  • border:简写模式,四边边框
  • border-width:边框宽度
  • border-style:边框样式,常用的为solid和dashed
  • border-color:边框颜色
  • border-top:上边框
  • border-right:右边框
  • border-bottom:下边框
  • border-left:左边框

这里来几个简单的例子,更详细的请参考各个属性文档。

p {
    /* 
    四边样式 
    1px为border-width
    solid为border-style
    #f00为border-color
    */    
    border: 1px solid #f00; 
}
div {
    border-top: 2px dashed #f00; /* 单边样式 */
}
span {
    border: 1px solid #ccc; /* 先定义四边样式 */
    border-top-color: #f00; /* 重新定义上边框的颜色 */
}
h1 {
    border: 1px dashed #999; /* 先定义四边样式 */
    border-width: 1px 2px; /* 重新边框的宽度 */
}

盒子内外边距

内边距为 padding,外边距为 margin,和 border 一样,也有四边可以设置,分别为上(top)、右(right)、下(bottom)、左(left),同样一般也采用简写的形式。

  • margin
  • margin-top
  • margin-right
  • margin-bottom
  • margin-left
  • padding
  • padding-top
  • padding-right
  • padding-bottom
  • padding-left

简单示例如下:

h1 {
    margin-top: 0; /* 外边距上 */
    margin-bottom: 20px; /* 外边距下 */
}
p {
    margin: 0; /* 外边距 */
}
div {
    padding: 15px 20px; /* 内边距,15px为上下值,20px为左右值 */
    margin: 0 20px 30px; /* 外边距,0为上,20p为左右,30px为下 */
}

盒子背景

设置背景图片,背景颜色,图片位置及是否平铺等,一般也采用简写形式。

  • background:总的简写形式,包括了下面各个单条属性
  • background-color:背景色
  • background-image:背景图片
  • background-position:背景图片起始位置
  • background-repeat:背景图片平铺方式
  • background-size:背景图片大小
  • background-clip:背景图片绘制区域
  • background-origin:背景图片的定位区域

简单示例如下:

p {
    background: #f00;
}
div {
    background: url(logo.png) no-repeat #fff;
}

盒子显示隐藏

  • overflow:指定当内容溢出其块级容器时,是否剪辑内容,渲染滚动条或显示内容
  • visibility:是否可见

盒子其他

  • border-radius:圆角
  • box-shadow:阴影

空间位置相关

这一块涉及元素定位布局。

  • display
  • float
  • clear
  • position
  • top
  • right
  • bottom
  • left
  • transform
  • z-index
  • opacity

动画相关

  • transition
  • animation

取值可分为关键字数字数字+单位多个值(这种情况下以排在前面的值最为优先,比如font-size: '微软雅黑', arial, sans-serif,如果没有字体就取默认字体)、颜色值等几个大类。

单位

两种典型的单位:

  • 像素(px、in、pt、mm、cm,绝对单位)
  • 百分比(%、em、vw、vh、rem,相对单位)

px

px 是 pixels(像素)的缩写,是一种绝对单位,用于屏幕显示器上,传统上一个像素对应于计算机屏幕上的一个点,而对于高清屏则对应更多。任何现代显示屏,不管是手机,平板,笔记本还是电视都是由成千上万的像素组成的,所以可以使用这些像素来定义长度。

另外 CSS 将光栅图像(如照片等)的显示方式定义为默认每一个图像大小为“1px”。 一个“600x400”解析度的照片的长宽分别为“600px”以及“400px”,所以照片本身的像素并不会与显示装置像素(可能非常小)一致,而是与 px 单位一致。如此就可以将图像完整的与网页的其它元素排列起来。

下面使用 px 单位设置下元素的大小,如下:

.box {
    width: 200px;
    font-size: 16px;
}

%

%(百分比)应该是最好理解的单位了,这里就不多解释了,主要有一点需要注意:

  • 如果对 html 元素设置 font-size 为百分比值,则是以浏览器默认的字体大小16px为参照计算的(所有浏览器的默认字体大小都为 16px),如62.5%即等于10px(62.5% * 16px = 10px)。

em

em 也是一种相对单位,既然是相对单位,那么肯定有一个参照值。不过其参照值并不是固定不变的,而是不同的属性有不同的参照值。

font-size

对于字体大小属性(font-size)来说,em 的计算方式是相对于父元素的字体大小,1em 等于父元素设置的字体大小。如果父元素没有设置字体大小,则继续往父级元素查找,直到有设置大小的,如果都没有设置大小,则使用浏览器默认的字体大小。

下面举几个简单的例子说明下:

.parent {
    font-size: 14px;
}
.child1 {
    font-size: 1em; /* 1em = 1*14px*/
}
.child2 {
    font-size: 1.5em; /* 1.5em = 1.5*14px */
}
/* 父级元素都没有设置大小的 */
.no-parent-font-size {
    font-size: 0.8em; /* 0.8em = 0.8*16px */
}

其他属性(border, width, height, padding, margin, line-height)

在这些属性中,使用 em 单位的计算方式是参照该元素的 font-size,1em 等于该元素设置的字体大小。同理如果该元素没有设置,则一直向父级元素查找,直到找到,如果都没有设置大小,则使用浏览器默认的字体大小。

下面举几个简单的例子说明下:

p {
    font-size: 14px;
    width: 20em; /* 20em = 20*14px */
    padding: 1.5em; /* 1.5em = 1.5*14px */
}
/* 元素本身没有设置字体大小且父级元素也没有设置 */
.no-font-size {
    width: 40em; /* 40em = 40*16px */
    margin-bottom: 2em; /* 2em = 2*16px */
}

总之 em 的计算单位相对来说比较复杂,现在已经不建议使用,如果要兼容的浏览器是现代浏览器的话,那么可以使用下面要介绍的 rem 单位。

rem

和 em 一样,rem 也是一种相对单位,不过不一样的是 rem 是相对于根元素 html 的 font-size 来计算的,所以其参照物是固定的。(rem的r就是表示root,虽然rem相对em进步了很多,但是由于是新技术,不支持IE8以下(包括IE8),不过幸喜的是移动端可以放心使用)

由于 rem 是基于跟元素 html 的 font-size 来计算的,所以如果改变 html 的 font-size 值,那么所有使用的 rem 单位的大小都会随着改变,这对于移动端适应各种屏幕大小来说还是有点作用的。

html {
    font-size: 625%; /* 相当于100px = 625% * 16px */
}
div {
    font-size: 20px; 
    width: 2rem; /* 2rem = 2 * 100px(根元素的font-size) */
    height: 4rem; /* 4rem = 4 * 100px(根元素的font-size) */
    padding: 0.1rem; /* 0.1rem = 0.1 * 100px(根元素的font-size) */
}

如果改变了 html 的 font-size 值,如设置为 80px,则相应的 div 的 width,height 和 padding 大小也随着改变了。

相对于 em 来说,rem 只需要修改 html 的 font-size 值即可达到全部的修改,即所谓的牵一发而动全身。

vw, vh, vmin, vmax

最后要介绍的这四个单位属于 v 系单位,它们也是相对单位,是基于视窗大小(浏览器用来显示内容的区域大小)来计算的。

网页中很多时候都需要用到满屏,或者屏幕大小的一半等,尤其是移动端,屏幕大小各式各样,而这个时候现有的单位就显得有点捉襟见肘,于是就诞生了这四个单位。

  • vw:基于视窗的宽度计算,1vw 等于视窗宽度的百分之一
  • vh:基于视窗的高度计算,1vh 等于视窗高度的百分之一
  • vmin:基于vw和vh中的最小值来计算,1vmin 等于最小值的百分之一
  • vmax:基于vw和vh中的最大值来计算,1vmax 等于最大值的百分之一

下面实例说明实现一个宽度为视窗宽度的 25%,高度为视窗高度 50% 的一个盒子:

.box {
    height: 50vh; /* 视窗高度的50% */
    width: 25vw; /* 视窗宽度的25% */
    background: red;
}

单位运算

除了设置以上的单位之外,还可以使用 calc 来进行单位运算,单位运算时可以使用各种单位进行加减乘除运算。

简单示例如下:

.box {
    height: calc(50vh - 20px); /* 50% 的视窗高度减掉20px */
    width: calc(100% / 3);  /* 三分之一的父容器宽度 */
    background: red;
}

注:chrome 浏览器最小的字体为 12px,如果设置 10px 也会渲染成 12px 。

颜色

取色工具

除此之外,打开 photoshop 和 sketch 也可以完成取色。

定义颜色

颜色关键词

首先,可以使用一些关键词来表示颜色,如 red,green,gray 等(默认浏览器支持 147 种关键词颜色:CSS 颜色名),除此之外,还有两个关键词可用,分别是 transparent 和 currentColor。

transparent 从字面上就可以知道是透明;而 currentColor 关键字表示使用该元素 color 的计算值。如果该元素设置了 color 颜色值,则使用该 color;如果该元素没有设置 color,则继承父级元素的 color。

实例如下:

/* transparent */
.transparent {
    border-color: transparent;
    color: transparent;
}
/* currentColor 使用场景一 设置了color */
.box {
    color: green;    
    border-color: currentColor; /* 使用color的颜色green */
}
/* currentColor 使用场景二 没有设置color 继承父级元素的color */
.parent {
    color: red;
}
.parent .child {
    border-color: currentColor; /* 使用来自parent的red */
}

rgb

表示使用红-绿-蓝模式来定义颜色。这其实就是十六进制(hex)和 rgb 函数两种形式。

十六进制

十六进制颜色表现形式为: #RRGGBB 和 #RGB

  • "#" 后跟6位十六进制字符(0-9, A-F)
  • "#" 后跟3位十六进制字符(0-9, A-F)

其中三位数的 RGB 符号是六位数的简写形式,当六位数满足两个 R,两个 G,两个 B 同时相等的时候,就可以进行简写。如#ff0000#336699#cccccc,可省略为#f00#369#ccc,但是#ff0122(表示绿色的01不一样),#3f3f3f(表示红色的3f不一样,同样表示绿色和蓝色也不一样)则不能省略。

rgb

rgb 函数表示为:rgb(red, green, blue)。每个参数 (red、green 以及 blue) 定义颜色的强度,可以是介于 0 与 255 之间的整数,或者是百分比值(从 0% 到 100%)。

rgb(255,0,0)表示红色,rgb(125,125,125)表示灰色。

rgba

在 rgb 的基础上,还可以添加一个 alpha 透明度表示半透明值,这样就构成了 rgba,其函数表示为:rgb(red, green, blue, alpha),其中 alpha 参数是介于 0.0(完全透明)与 1.0(完全不透明)的数字。

如需要一个半透明的黑色作为遮罩层:background: rgba(0, 0, 0, 0.5)

hsl

除了使用红-绿-蓝的模式定义颜色之外,还可以通过 hue(色调)、saturation(饱和度)、lightness(亮度)模式定义颜色,其语法为:hsl(hue, saturation, lightness)

Hue 是色盘上的度数(从 0 到 360) - 0 (或 360) 是红色,120 是绿色,240 是蓝色。Saturation 是百分比值;0% 意味着灰色,而 100% 是全彩。Lightness 同样是百分比值;0% 是黑色,100% 是白色。如红色使用 hsl 表示:hsl(0, 100%, 50%)

下面通过图来帮助理解下:

同样对于 hsl 也可以加入透明度来表示半透明色,这就是 hsla,语法为hsla(hue, saturation, lightness, alpha),alpha 取值同样为介于0.0到1.0的数字,如hsla(0, 100%, 50%, 0.7)

参考资料

盒模型

每个元素都被描绘成矩形盒子,这些矩形盒子通过一个模型来描述其占用空间。

margin——外边距、border——边框、padding——内边距、content——内容区域

块级元素具有上下左右方向的值,而行内元素只有左右方向的值,没有上下方向的值。

box-sizing 属性

box-sizingcontent-box,就是标准盒模型

大部分浏览器(IE除外)在默认条件下,实际内容宽高为设置的 widthheight ,也就是说 box-sizing 的默认值为content-box。如果设置了 box-sizingborder-box,则实际内容的宽高要减去对应的 borderpadding 值。

margin、padding属性

属性按方向分开写可以为margin-top、margin-left、margin-bottom、margin-left

上下左右都设置为10px

.box-1 {
    margin: 10px;
}

按上、右、下、左顺序设置

.box-2 {
    margin: 10px 20px 30px 40px;
}

按上、左右、下顺序设置

.box-3 {
    margin: 10px 20px 30px;
}

按上下、左右顺序设置

.box-4 {
    margin: 10px 20px;
}

border 属性

border-widthborder-styleborder-color属性。

对上下左右 border 分别取值时,分为上面这三个属性,属性内的取值规则和 margin、padding 一样。

.box-1 {
    border-width: 2px 1px;/* top和bottom,left和right */
    border-style: solid;/* all */
    border-color: #f00 #ccc #ccc;/* top、left和right、bootom */

也可按方向分,与 margin、padding 一致。

按方向分后,如border-top,还可进一步拆分为border-top-widthborder-top-styleborder-top-color属性。

元素的显示和隐藏

display

值为 none 时:

  • 所有的后代元素都隐藏
  • 好像这个元素不存在一样

visibility

值为 hidden 时:

  • 元素的大小不变,可理解为透明
  • 子元素设为visibility: visible,则该子元素依然可见

overflow

  • 规定了当内容元素溢出时父容器的展现形式
  • 裁剪内容,使用滚动条(auto)来显示或直接显示或隐藏(hidden)超出部分

背景

常见属性:

.box {
    height: 400px;
    background-color: #e8e8e8;/* 背景颜色 */
    background-image: url('bg.jpg');/* 背景图片 */
    background-repeat: no-repeat;/* 背景图片平铺方式 */
    background-position: center center;/* 背景图片定位 */
    background-size: 100px 100px;/* 背景图片大小 */
}

简写:

.box {
    height: 400px;
    background: #e8e8e8 url('bg.jpg') no-repeat center center/100px 100px;
}

渐变背景

线性渐变

从上到下的线性渐变

.gradient-default {
    background: linear-gradient(orange, yellow);
}

从左到右的线性渐变

.gradient-utd {
    background: linear-gradient(to right, orange, yellow);
}

使用角度的线性渐变

.gradient-angel {
    background: linear-gradient(135deg, orange, yellow);
}

指定多个等间距的色标

.gradient-colors {
    background: linear-gradient(to right, red, orange, yellow, white);
}

指定多个等间距的色标

.gradient-colorposition {
    background: linear-gradient(to right, orange, yellow 70%, red);
}

使用透明度

.gradient-opacity {
    background: linear-gradient(to right, rgba(0, 0, 255, 0), rgba(0, 0, 255, 1));
}

重复的线性渐变

.gradient-repeat {
    background: repeat-linear-gradient(-45deg, blue, blue 5px, white 5px, white 10px);
}

径向渐变

等间距色标

.gradient-equal {
    background: radial-gradient(red, yellow, rgb(30, 144, 155));
}

指定间距色标

.gradient-colorposition {
    background: radial-gradient(red 5%, yellow 15%, rgb(30, 144, 155) 60%);
}

指定中心点位置

.gradient-colorposition {
    background: radial-gradient(at 30px 30px, red 5%, yellow 15%, rgb(30, 144, 155) 60%);
}

指定渐变的形状(shape)

.gradient-shape1 {
    background: radial-gradient(circle, red, orange, yellow);
}
.gradient-shape2 {
    background: radial-gradient(red, orange, yellow); /* 默认是椭圆 */
}

指定渐变的尺寸(size)

.gradient-size1 {
    background: radial-gradient(circle closest-side, red, orange, yellow);
}
.gradient-size2 {
    background: radial-gradient(circle farthest-side, red, orange, yellow);
}
.gradient-size3 {
    background: radial-gradient(circle closest-corner, red, orange, yellow);
}
.gradient-size4 {
    background: radial-gradient(circle farthest-corner, red, orange, yellow);
}

重复的径向渐变

.gradient-repeat {
    background: repeating-radial-gradient(red, orange 10%, yellow 25%);
}

图片

图片作为网页必不可少的一部分,在网页中占据着非常重要地位。一般来说,有以下两种方式来使用图片:

  • 通过 img 元素直接使用
  • 通过 background-image(背景图片)的形式使用

这两种形式的区别在于,前者一般具有实际含义(如产品图片,相册图片等),而后者一般用于装饰效果。

图片初步了解

目前网页中常用的图片大概有如下几种格式,它们有着各自的显著特点,被应用在各种不同的场景:

  • jpg/jpeg:由于其色彩还原度比较好,所以一般色彩丰富的图片均采用该格式,如宣传图、产品图、相册图等等。(其实相机拍出来的照片就是该格式的)
  • png:由于其对透明度的良好支持,所以一般用于透明图片,如 logo 图、图标图等
  • gif:由于其对动画的支持,所以一般用来实现动效图片,如 loading 加载动画、一些搞笑图片等

除了这三种图片格式外,还有 ico 格式和 webp 格式:

  • ico 格式属于图标文件,主要用于网址前面的标识图标

  • webp 格式是由 google 研发的图片格式,它既具备高压缩率,又具备透明度以及动画的特性。目前各个大互联网公司都有在使用该格式

参考资料

图片优化

雪碧图

在网站开发中,经常会使用一些背景图片来点缀效果,如一些形象生动的小图标。这种背景图片一多,网络请求就多了。这样为了减少网络请求,把一些小的背景图合并在一个大的图中,然后通过 CSS 的背景定位技术去使用。这种技术叫做 CSS Sprite,也叫雪碧图,还叫 CSS 精灵。

雪碧图的应用原理

雪碧图是一张大的合并图,每个小图标其实只是截取大图的一部分来显示。如下图所示,有一张带有各种表情图标的雪碧图,其中每个表情图标都占领着相应的位置。

假如需要显示开心表情的这一个图标,需要计算开心图标在合并图中相应的位置和其图标的大小。以雪碧图的左上角为坐标中心,得出开心表情刚好在坐标的原点,即 X 轴 0 像素,Y 轴 0 像素的位置,且可以得出图标的宽高均为 96 像素。

因此设置这个图标元素的背景图片为这张雪碧图,背景位置为图标在雪碧图中的坐标,这样开心的图标就显示出来了。

.happy {
    display:inline-block; 
    width: 96px;
    height: 96px;
    background-image: url(sprite.png);  /* 设置背景图片 */
    background-repeat: no-repeat; /* 设置为不平铺 */
    background-position: 0 0;   /* 设置图标位置 */
}

假如还要使用到哭泣的表情。同理所得,计算出哭泣的图标的位置在雪碧图的 X 轴向右 192 像素, Y 轴向下 96 像素。

因此为了显示哭泣表情,可以这样写:

.cry {
    display:inline-block; 
    width: 96px;
    height: 96px;
    background-image: url(sprite.png);  /* 设置背景图片 */
    background-repeat: no-repeat; /* 设置为不平铺 */
    background-position: -192px -96px;   /* 设置图标位置 */
}

上面的例子已经阐述了如何使用雪碧图,不过初学者可能会对哭表情 background-position 为负值有所疑惑。为了说明这个问题,先看下日常生活中的放大镜:

平时使用放大镜一般拿着放大镜移动以查看对应的放大效果,下面的东西不动。现在假设如果放大镜不动,移动下面的东西是不是也可以查看对应的放大效果呢?

如果把要显示图标的区域(元素大小)当做是放大镜,它是不动的,而背景图片就是要放大的东西,为了要显示对应的图标就得移动了。前面已经说了 background-position 的坐标系:向右为 X 轴正值,向下为 Y 轴正值。

现在要使用哭的表情,其实就是把它从(192px, 96px)移到(0, 0)那个位置(元素的位置,相当于放大镜的默认位置),这样就要向左移动192px,向上移动96px。根据向右、向下为正,那么向左、向上移动自然就是负了。

最后整理下,发现可以把共同的部分抽出来定义一个 class,这样就不用去定义每一个表情的背景图片了,如下所示:

.sprite {
    display:inline-block; 
    width: 96px;
    height: 96px;
    background: url(sprite.png) no-repeat;  /* 设置背景 */
}
.happy {
    background-position: 0 0;   /* 设置图标位置 */
}
.cry {
    background-position: -192px -96px;   /* 设置图标位置 */
}

制作雪碧图

制作雪碧图的方式有许多,大概有如下几种方法:

  • 使用 photoshop 等图片编辑工具

对于简单的雪碧图,可以通过 photoshop 等图片编辑工具来手动合并图标到一张大图中。

  • 在线工具

目前有许多在线合成雪碧图的站点,只需要将图片上传上去,便可以根据设置,生成想要的雪碧图以及对应的 CSS 样式文件,如 spritegencss sprites generator

甚至合好的图片,也可以通过 spritecow 来帮完成对应的 CSS 定位。

  • 构建工具

除了上面两种,还可以通过如webpack, fis3, gulp等构建工具来完成雪碧图生成工作。具体可参考下面链接:

雪碧图与字体图标优劣

前面已经说了可以使用 iconfont 来搞图标,现在又介绍了雪碧图,那么这两种办法对一些图标的处理都有什么优劣呢?

总得来说,使用 iconfont 或者雪碧图都存在着相应的优点和缺点。因此需要结合各自的优劣,来综合考虑使用哪种方案。

雪碧图的优势

  • 图标更美观

由于 iconfont 的图标只能设置单色,而雪碧图的图标由于是图片,所以能展现出更为美观的图标效果。

  • 制作成本更低

iconfont 的制作较为麻烦,需要设计师按照规范,一一制作图标的 svg 文件。而雪碧图的制作只需合并图片即可,在制作成本上更低。

雪碧图的劣势

  • 高清屏下会失真

在 2x 的设备像素比的屏幕上,如果要达到和文字一样的清晰度,图片的宽度需要实际显示大小的两倍,否则看起来会比较模糊。

  • 雪碧图不方便变化

雪碧图本质上是一张静态的图片,因此无法灵活地通过样式去动态改变图片图标的颜色和其他效果。

样式计算

浏览器的默认样式

浏览器的自带样式,称之为默认样式。

样式的继承

属性越通用,被继承的可能性就越高。

样式的层叠

  • 元素声明的样式的权重高于浏览器默认样式
  • 浏览器默认样式的权重高于继承的样式

样式优先级

!important(最高级别,不能滥用) > style > id 选择器 > 类选择器、伪类选择器、属性选择器 > 元素选择器、伪元素选择器 > *(通用选择器) > 浏览器默认 > 继承

加上关系选择器

如,下面两个选择器作优先级对比:

.text {
    color: green;
}

body p {
    color: chocolate;
}

上边比下边优先级高,原因是不管有多少个选择器,按照最高级的那个选择器做对比。

再比如:

.title span {
    color: red;
}

.title span.little {
    color: green;
}

.title .little {
    color: blue;
}

第二个选择器优先级最高,如果优先级一样就可抵消掉,然后继续做对比。

值的计算

一个元素的样式可能来自三个部分:

  • 继承自父级元素的样式
  • 元素的浏览器默认样式
  • 元素自己声明的样式

而最终应用的样式就是这三个部分通过一套复杂的计算体系得到的。

应用值

把最终应用的样式称之为应用值(used value)。

如果元素本身声明了样式,那么应用值将采用元素声明的样式,这点没有什么问题了。但是如果元素没有声明该样式呢,它究竟用什么值来当做最终的应用值?

这就得回到 CSS 属性定义了。

每个 CSS 属性在定义的时候都指出了该属性是默认继承的还是默认不继承的(下面附加了些常见的继承属性及非继承属性)。这就决定了如果元素没有声明该样式,怎么得到最终的应用值:

  • 如果属性是默认继承的,则取父元素的同属性的应用值(computed value)。通过该方式得到的值称之为继承值(inherit value)
  • 如果属性是默认不继承的,则取该属性的初始值(initial value)

这样,除应用值外,又有了两种新值,它们分别为继承值(inherit value)和初始值(initial value)。

继承值

为了进一步说明继承值,举个简单的例子来说明下,代码如下:

<p class="red" style="color: red;"><span>我继承了父元素的红色</span></p>

<p>元素设置了文本颜色为红色,<span>没有任何设置。

在计算<span>元素的最终颜色时,因为该元素本身没有声明color属性,且由于color属性属于默认继承类型的,所以会取父元素color属性的应用值(红色)。这样<span>通过父元素继承过来的值就是红色,而最后的应用值用得就是该继承值。

当然还可以通过显式设置color属性的值为继承值,如color: inherit;

注:inherit关键字用于显式地指定继承性,可用于继承性/非继承性属性。

初始值

每个属性都会有一个默认的初始值,如width属性的初始值为autofont-size的初始值为medium

初始值针对不同类别的属性具有不同的含义:

  • 对于继承属性,初始值只能被用于没有指定值的根元素(HTML)上
  • 对于非继承属性,初始值可以被用于任意没有指定值的元素上

在CSS3中允许使用initial关键词明确的设定初始值。如font-size: initial;就是使用初始值。

其余值

除了上面的三种值之外,其实还有几种值的概念,如指定值(Specified value)、计算值(computed value)等等,具体可参看:Assigning property values, Cascading, and Inheritance

默认没有一个官方地址查看所有的继承属性或非继承属性,不过可以单独查看某个属性是否属于继承或非继承属性,如在 MDN 的参考手册中查看color属性,在“是否是继承属性”那一栏就写了“yes”,截图如下:

常见继承属性

  • 文本相关属性都可以继承

color、font、font-family、font-size、font-style、font-variant、font-weight、text-decoration、text-transform、letter-spacing、word-spacing、white-space、word-break、overflow-wrap、line-height、direction、text-indent、text-align、text-shadow

  • 列表相关属性

list-style-image、list-style-position、list-style-type、list-style

  • 表格相关属性

border-collapse、border-spacing

  • visibility 和 cursor

常见非继承属性

  • 盒模型相关属性

margin、border、padding、height、min-height、max-height、width、min-width、max-width、box-sizing

  • 布局类属性

display、overflow、position、left、right、top、bottom、z-index、float、clear、table-layout、vertical-align

  • 系列类

background 系列、transform 系列、transtion 系列、animation 系列、flexbox 系列、grid 系列

CSS 重置

常见的两种重置方式:

  • normalize.css(纠正)

问题:可能不是想要的。

  • Eric Meyer's "Reset CSS" 2.0(清零)

问题:一竿子打死了。

较理想的重置方式:normalize.css + 部分清零

浏览器兼容

首先浏览器有很多种,每种浏览器会存在一定的差异,其次每个浏览器都有不同的版本,版本之间也存在必然的差异,而做出来的页面则需要各个浏览器以及不同版本表现一致,所以必然存在兼容问题。

一般来说兼容问题可以分两步走:第一步是确定浏览器是否支持,第二步是如果表现不一致,怎么去修复。

浏览器是否支持

技术总是在不断改进和发展的,新的东西一出来,那老的一些版本浏览器可能就不支持了。就如手机的发展,总不能让以前的功能机安装智能机的 APP 吧。

一般来说,由于技术的不断改进和发展,大概存在以下几种问题:

  • 新技术在老版本的浏览器总是不支持的(如ie8以下对 CSS3 支持几乎是空白)
  • 由于新技术刚出现时,可能还没有完全定稿标准化,所以各个新浏览器一般都是先试探性的使用前缀的办法使用
  • 同样的技术在不同的浏览器上可能表现也不一样。

对于这些问题,可以查阅 Can I use ,里面提供了各种浏览器支持情况。

以 CSS 的 flexbox 为例,在搜索框输入关键词”flexbox”,下面就显示了其对应的浏览器支持情况。

在查阅 Can I use 的时候,可以看到有些版本的右上角标有-符号,这就标识该版本得使用前缀,目前常见的前缀有-webkit(webkit内核浏览器)、-ms(ie/edge)、-moz(火狐浏览器)

这里以圆角(border-radius)为例说明下前缀用法(当然现在圆角已经不需要写前缀了):

.box {
    -webkit-border-radius: 5px; /* webkit内核浏览器(chrome、safari) */
    -moz-border-radius: 5px; /* 火狐浏览器 */
    border-radius: 5px; /* 标准语法 */
}

如何针对修复

如果问题出现了,怎么针对某些浏览器进行特定的修复而不影响到其他正常的浏览器。

这个时候就可以参考各个浏览器hack详细,里面提供了针对各种浏览器单独写样式的很多方法(不一定所有办法都可以,但是可以挑选一个可以的)

以修复 IE6、7 不兼容 inline-block 为例,挑选一条如下在属性前面加*的修复:

最终代码如下:

.inline-block {
    display: inline-block;
    *display: inline;
    *zoom: 1;
}

列表样式基础

列表在网页中使用相当广泛,几乎遍地都是。但是矛盾的是,几乎从来不用其默认样式。所以为了方便干掉列表的默认样式就成了势在必行,其重置如下:

// ul ol dl
ul, ol, li, dl, dd {
    margin: 0;
    padding: 0;
}

ul, ol {
    list-style: none outside none; /* type, position, image*/
}

其中list-stylelist-style-typelist-style-positionlist-style-image三个属性组成。虽然list-style-image可以用来定义列表的前面的小图标,然而实战中需要设置列表前面的小图标时,一般都使用 background 来实现,所以这个属性几乎是个鸡肋。

省略

省略的详细实现可参考:CSS实现单行、多行文本溢出显示省略号(…)

单行省略

关键属性:text-overflow

overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

多行省略

display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;

常用图标库

表格样式基础

在没有 CSS 年代,表格的正确使用方法是通过直接在 table 元素上设置属性来实现效果的:

<table border="1">
  <tr>
    <th>Month</th>
    <th>Savings</th>
  </tr>
  <tr>
    <td>January</td>
    <td>$100</td>
  </tr>
</table>

效果图如下:

当然有了 CSS 之后,样式肯定是交给 CSS 来负责了,现在把上面 table 的 border 属性去掉,通过 CSS 来设置 table 的 border,如下:

table {
    border: 1px solid #ccc;
}

效果图如下:

怎么只有外面一层有样式呢?跟上面设置属性的完全不同啊,这是因为只是使用 CSS 选中了 table 元素,而如果设置里面的线条应该是由 td、th、tr 元素来设置的。现在接着设置 th 和 td 的 边框试试:

th, td {
    border: 1px solid #ccc;
}

效果图如下:

这样的效果就跟在 table 元素上直接设置 border 属性差不多了,但是这明显也不是所要的效果,于是需要重置 table 如下(边框间隙为 0,边框合并):

table {
    border-spacing: 0; /* 边框间隙为0 */
    border-collapse: collapse; /* 边框合并 */
}

效果如下:

除了把 table 重置之外,其实 th,td 还有一个默认的 padding 值,也需要重置为 0 :

td,
th {
    padding: 0;
}

除了这些基础的重置之外,表格布局算法也是一件比较纠结的事,其属性为 table-layout,取值有:

  • auto:自动布局,列的宽度是由列单元格中没有折行的最宽的内容设定的
  • fixed:固定布局,列的宽度取决于表格宽度、列宽度、表格边框宽度、单元格间距,而与单元格的内容无关,与自动布局相比,浏览器可以更快得对表格进行布局

更多说明可参看:CSS table-layout 属性

默认如果不设置table 及列的宽度,不论使用什么布局,table的宽度及列的宽度都由内容撑开。

而如果对 table 设置一个宽度,如“100%”,则列宽度存在以下几种情况:

  • 没有任何列设置宽度,则列的宽度由其内容按比例分配
  • 如果有某一列或几列设置了宽度,则剩余的由其内容按比例分配
  • 如果每列都设置了宽度,每列的宽度加起来不等于table的宽度,则按照列设置的宽度比例重新分配宽度,而不是实际列设置的宽度

如果在设置 table 宽度的前提下,再设置其为固定布局,则列宽度存在以下几种情况:

  • 没有任何列设置宽度,则宽度等分
  • 如果有某一列或几列设置了宽度,则剩余的宽度等分
  • 如果每列都设置了宽度,则按设置的比例分配宽度

表单样式基础

在所有的元素中,如果说表单元素样式的复杂性排第二,那么没有其他元素敢称第一。

基础表现一致

normalize.css 对表单元素样式表现一致性的纠正情况,可以看出其十分复杂

盒模型计算方式

不同的表单元素盒模型的计算方式不太一样,多数是默认的“content-box”,但是还有些是“border-box”,如 chrome 浏览器对按钮就设置 border-box。所以为了以后的计算方便,还是统一设置为border-box方便。

focus 样式

webkit 内核浏览器会自动添加 focus 样式, 效果如下:

而这一般在项目中都是要去掉的,代码如下:

input,
select,
textarea,
button {
    outline: 0;
}

样式不可控

  • 单选框(radio)及复选框(checkbox)的大小,颜色等都不可控,现在一般流行使用伪元素来设置该样式,覆盖默认的丑陋样式(如),或者使用 JS 来模拟单选框及复选框
  • select 选项样式没法设置样式,所以如果不使用默认的又得用 JS 来搞
  • 上传文件(file)各个浏览器表现不一致,而且几乎不可以美化(最多只能搞成一个按钮形式)
  • placeholder IE9- 不支持,如果要兼容 IE9- 则需要 JS 来实现兼容
  • 行高及高度未解之谜。有些元素某些浏览器设置行高无效,同样有些是高度无效,所以要实现一个高度且文字垂直居中最保险的办法是设置上下padding
  • 更多 HTML 5 新加的各种表单元素的兼容问题

下面是一些设置样式的参考资料:

CSS 计数器

使用CSS计数器

CSS 布局

表格布局

最有荒古气息的是表格布局。

缺点:

  • 加载慢,必须等到整个表格布局都加载完了,才展现给用户;
  • 使用<table />标签,不用来展示数据,却用来布局,不符合 HTML 语义化。

DIV + CSS 布局

主流布局,较为灵活,可以实现多种形式的布局展现形式。

HTML 语义化

优点:

  • 方便团队开发与维护
  • 有利于SEO
  • 屏幕阅读软件(盲人)会根据结构来读页面

HTML 语义当然不仅仅只是几个 HTML 语义标签。

从“文档”说起

很多时候会把“页面”和“文档”这两个词混着用,比如将 HTML 页面说成“HTML 文档”。

HTML 就是文档,《Web 简史》中有提到过,万维网的雏形是一个文档共享系统,万维网就是一个放大版的文档共享系统。

只是随着 Web 的发展,各种酷炫的页面和应用层出不穷,倒是让小伙伴忽略了,HTML 的本质其实是文档(document)。

现实生活中,说到文档,第一反应就是 Microsoft Word ,其实 Word 当年也有可能成为 Web 标准,当然这是另外一个故事。

文档大纲

Word 中的标题有一级标题、二级标题……的说法,所有的标题就构建出了这个 Word 文档的大纲。下图的左边栏就是大纲视图,可以很好的观察文档大纲。

word-outline (2).jpg

在 HTML 文档中呢,H1 ~ H6 就是一级到六级标题,它们构成了 HTML 文档的大纲。一个好的页面,必定先是一个好文档,好文档必然有着严谨的文档大纲。

可以使用谷歌浏览器查看页面文档大纲的插件,html5-outliner

再来看“万维网”

最初的万维网是一个文档共享的网络,现在的万维网则是一个资源共享的网络,包括图片、多媒体等等。

HTML 则是万维网的粘合剂,也是万维网的载体,但是现在 HTML 给人们的感觉啊,就是给人看的,其实,HTML 同时也会给机器看,比如下图,除了 human 在读 HTML ,各式各样的机器也在读 HTML ,比如搜索引擎的爬虫和读屏设备。

视觉上的各种酷炫会给人以视觉冲击,但对机器来说,并没有什么用,它们更看重的是语义,这样才能更好地解析内容。这也是为什么样式会从结构里面分离出来的原因之一。

exploiting-html.jpg

HTML 语义

HTML 本身这个层面上,语义也有更多的东西,关于这些有一些参考资料,参考如下:

让 IE8 支持 HTML5 语义化标签

HTML5 是 HTML 最新的修订版本,于 2014 年 10 月由万维网联盟(W3C)完成标准制定。而 IE8 面世时间为 2009 年 3 月 19 日,时间相差如此之大,所以 IE8 作为比较古老的浏览器,不支持 HTML 5 引入的语义化标签(如 header、nav、menu、section、article 等)也是很正常的。

默认情况下 IE8 对 HTML5 标签的处理

在 IE8 里面,未定义的标签 —— IE8 不认识所有新引入的 HTML5 标签,所以定义样式是不会生效。比如下面这段代码(抽取主要代码):

<style>
  section { color: red; }
</style>

<section>
  hello world
</section>

期待展示的效果如下:

Css _ie8_1 .png

但在 IE8 中实际展示效果如下:

Css _ie8_2.png

如何让 IE8 支持 HTML5 标签

虽然默认不支持,但是可以通过 JS 使用 document.createElement 来“欺骗” IE 的 CSS 引擎,让它知道某个标签的存在,具体做法如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>HTML5 test</title>
  </head>
  <body>

    <script>
      document.createElement('section');
    </script>

    <style>
      section { color: red; }
    </style>

    <section>
      Hello!
    </section>

  </body>
</html>

显示效果如下:

Css _ie8_3.png

既然元素默认都不支持,就更没有相关默认的样式了,所以还要加上一些重置样式如下:

article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary {
  display: block;
}

私有化后台原型

借助 html5shiv.js 让 IE8 支持更多的 HTML5 特性

其实不只是 IE8 , IE6-9、 Safari 4.x (以及 iPhone 3.x)、还有 Firefox 3.x 等等,对 HTML5 的支持都不完善。所以有了一个库 html5shiv.js 来做统一处理,shiv 意为用作武器的小刀(实际上是一个拼写错误,应该为 shim),在机械工程中的专业释义为垫片,比喻给那些老旧的浏览器加个垫片,让它们基本能用。

更多阅读

显示类型 —— display

一个元素是块级元素还是行内元素,本质上是一个 CSS 特性。

和布局关系紧密,和元素显示方式息息相关。

display: block / table / list-item ...

显示块级元素的特征。

display: inline / inline-block ...

显示行内元素的特征。

块级元素和行内元素可以互相转换。

block

  • 每一个都另起一行
  • 可设置宽高、行高、上下边距

inline

  • 和其它行内元素同一行
  • 不可设置宽高、行高、上下的边距

inline-block

既有 block 的特征,也有 inline 的特征

取值

基本值

none, inline, block, inilne-block

flexbox

flex, inline-flex

grid

grid, inline-grid

table

table, table-row, table-cell

表格类特性:设置tabletable-cell,子元素不换行,且 margin 无效,会挤在一起;设置 verticla-align 有效。

视觉格式化模型 (visual formatting model)

在盒模型 (box model) 里,元素会被渲染成一个个盒子。那么这些盒子在屏幕上的位置又是怎么放置的呢?这就是—— CSS 视觉格式化模型 (visual formatting model)。视觉格式化模型是 CSS 布局的一个基础理论体系。

盒子的位置摆放

默认情况下,盒子按照元素在 HTML 中的先后位置从左至右自上而下一个接着一个排列摆放。如下图:

Css_vfm_default.png

在图中可以看到,有些元素的盒子被渲染为完整的一行,如 h1、p、div;而有些元素的盒子则被渲染为水平排列,直到该行被占满然后换行,如 span、a、strong。

这是因为不同的盒子使用的是不同的格式化上下文 (formatting context) 来布局,每个格式化上下文都拥有一套不同的渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。(就如参加结婚喜宴一样,有父母长辈席,好友席,同事席,甚至前男/女朋友席等,不同的身份坐到对应位置即可。)

格式化上下文 (formatting context)

常见的两个格式化上下文分别为:块格式化上下文(block formatting context 简称 BFC)和行内格式化上下文(inline formatting context 简称 IFC)

BFC

块级盒 (block-level boxes)

当元素的 CSS 属性 display 的计算值为 block,list-item,table,flex 或 grid 时,它是块级元素。视觉上呈现为块,竖直排列。典型的如 <div> 元素,<p> 元素等都是块级元素。

每个块级元素至少生成一个块级盒,称为主要块级盒 (principal block-level box) 。一些元素,比如 <li>,生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒。

块级盒参与 BFC,被渲染为完整的一个新行。

渲染规则

默认根元素(html 元素)会创建一个 BFC,其块级盒子元素将会按照如下规则进行渲染:

  • 块级盒会在垂直方向,一个接一个地放置,每个盒子水平占满整个容器空间
  • 块级盒的垂直方向距离由上下 margin 决定,同属于一个 BFC 中的两个或以上块级盒的相接的 margin 会发生重叠
  • BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此
  • 计算 BFC 的高度时,浮动元素也参与计算

除此之外,还有其他方法可以创建一个新的 BFC,具体可参看:块格式化上下文。除此之外,flexbox 布局和 grids 布局中的 item 都会创建一个新的 BFC。

具体渲染规则及效果可参看:块格式化上下文

Dingtalk_20220423145005.jpg

Dingtalk_20220423145032.jpg

Dingtalk_20220423145049.jpg

Dingtalk_20220423145059.jpg

Dingtalk_20220423145107.jpg

更多关于 BFC 相关内容可参看:

IFC

行内级盒(inline-level boxes)

当元素的 CSS 属性 display 的计算值为 inline,inline-block,inline-table,inline-flex 或 inline-grid 时,它是行内级元素。视觉上它将内容与其它行内级元素排列为一行,直到该行被占满然后换行。典型的如段落内容,文本或图片,都是行内级元素。

严格来说,行内元素不包括 inline-block 的,行内级元素才包括。要理解其中的区别,知晓这个美丽的错误。

行内级元素生成行内级盒,参与行内格式化上下文(inline formatting context),被渲染为水平排列, 直到当行被占满然后换行。

行内级盒分为行内盒(inline boxes)和原子行内级盒(atomic inline-level boxes)。前者由非置换元素且 display 值为 inline 的元素生成;后者由行内级置换元素,或 display 值为 inline-block, inline-table, inline-flex, inline-grid 的元素生成。

每一行排列的行内级盒都可以看做由一个匿名的行盒包裹,如下图(使用了两种灰色背景色来模拟):

Dingtalk_20220423141458.jpg

渲染规则

当块容器盒(block container box)不包括任何块级盒(block-level boxes)时,就会创建一个行内格式化上下文(IFC)。(一般来说一个块级盒也是一个块容器盒,具体可参看: Block-level elements and block boxes

IFC 中的行内级盒将会按照如下规则进行渲染(规则有点多,大概主要点就是行盒,折行机制,水平对齐方式,垂直高度及垂直对齐方式):

  • 盒子一个接一个地水平摆放,当容器宽度不够时就会换行
  • 在水平方向上,这些盒的外边距、边框、内边距所占用的空间都会被计算,但行内盒的垂直的border,padding 与 margin 都不会撑开行盒的高度
  • 在垂直方向上,这些盒可能会以不同形式来对齐,可通过 vertical-align 来设置,默认对齐为 baseline
  • 每一行将生成一个行盒(line box),包括该行所有的盒子,行盒的宽度是由包含块和存在的浮动来决定
  • 行盒一般左右边都贴紧其包含块,但是会因为浮动盒(float 元素)的存在而发生变化。浮动盒会位于包含块边缘与行盒边缘之间,这样行盒的可用宽度就小于包含块的宽度
  • 当所有盒的总宽度小于行盒的宽度,那么行盒中的水平方向排版由 text-align 属性来决定
  • 当所有盒的总宽度超过一个行盒时,就会形成多个行盒,多个行盒相互之间垂直方向不能分离,不能重叠
  • 当一个行内盒超过行盒的宽度时,它会被分割成多个盒,这些盒被分布在多个行盒里。如果一个行内盒不能被分割(比如只包含单个字符,或word-breaking机制被禁用,或该行内框受white-space属性值为nowrap或pre的影响),那么这个行内盒将溢出这个行盒
  • 当一个行内盒发生分割时,分割处的 margins, borders 和 padding 不会有任何视觉效果(或者其他任何分裂,只要是有多个行盒)
  • 行盒的高度由内部元素中实际高度最高的元素计算出来。每个行盒的高度由于内容不一样,所以高度也可能不一样
  • 在一个行盒中,当他包含的内部容器的高度小于行盒的高度的时候,内部容器的垂直位置可由自己的 vertical-align 属性来确定

注:在 IFC 的环境中,是不能存在块级元素的,如果将块级元素插入到 IFC 中,那么此 IFC 将会被破坏掉变成 BFC,而块级元素前的元素或文本和块级元素后的元素或文本将会各自自动产生一个匿名块盒其包围。

具体行盒高度及垂直对齐方式渲染效果可参看:

Dingtalk_20220423144020.jpg

Dingtalk_20220423144233.jpg

Dingtalk_20220423144358.jpg

Dingtalk_20220423144420.jpg

Dingtalk_20220423144431.jpg

Dingtalk_20220423144441.jpg

Dingtalk_20220423144450.jpg

更多关于 IFC 相关内容可参看:

其他格式化上下文

除此之外,还有一些其他不同类型的盒子,如下:

  • 表格布局:可以创建一个表格包裹盒(table wrapper box),包括了表格盒(table box)及任何标题盒(caption boxes)。
  • 多列布局:可以在容器盒与内容之间创建列盒(column boxes)
  • 弹性布局:将会创建一个弹性容器盒(flex container box)
  • 网格布局:将会创建一个网格容器盒(grid container box)

而这些盒子也将采用不用的格式化上下文来渲染,如 table formatting context(table 布局)、flex formatting context(flexbox 布局)、grid formatting context(grid 布局)。

更多关于盒子的介绍可参看:

定位方案

上面所讨论的其实都是常规流(normal flow)中盒子的摆放。但实际上有三种定位方案,分别为:

  • 常规流(normal flow):盒一个接一个排列,不同的盒子采用不同的格式化上下文渲染。
  • 浮动(float):盒将从常规流里提出来,放在当前盒的旁边。
  • 绝对定位(absolute positioning):盒将脱离常规流,其坐标是绝对的(通过 top / bottom / left / right 来设置)。

常规流(normal flow)

默认盒的定位方案就是常规流,但是如果触发了以下任何一个条件,将不会使用常规流:

  • position 的值非 static 或 relative
  • float 的值非 none

在常规流中,不同的盒子将采用不同的格式化上下文渲染,也就是上面所讲的部分。

浮动(float)

对于浮动定位方案, 盒称为浮动盒(floating boxes)。它位于当前行的开头或末尾。这导致常规流环绕在它的周边,除非设置 clear 属性。

要使用浮动定位方案,元素 CSS 属性 position 必须为 static 或 relative,然后 float 不为 none 。如果 float 设为 left, 则浮动定位到当前位置的开始位置,如果设为 right, 则浮动定位到当前位置的最后位置。

绝对定位(absolute position)

如果元素的属性 position 不是 static 或 relative, 那它就是绝对定位元素。

对于绝对定位方案,盒从常规流中被移除,不影响常规流的布局。 它的定位相对于它的包含块,定位坐标可通过属性 top、bottom、left、right 来设置 。

固定定位元素(fixed positioned element)也是绝对定位元素,它的包含块是视口。当页面滚动时它固定在屏幕上,因为视口没有移动。

参考资料

深入了解 inline-block

一些使用 inline-block 产生的问题和解决方法以及其常见的应用场景。

水平间隙问题

创建一个导航列表,并将其列表 item 设置为 inline-block,主要代码如下:

<div class="nav">
  <div class="nav-item"><a>导航</a></div>
  <div class="nav-item"><a>导航</a></div>
  <div class="nav-item"><a>导航</a></div>
</div>
.nav {
  background: #999;
}
.nav-item{
  display:inline-block; /* 设置为inline-block */
  width: 100px;
  background: #ddd;
}

效果图如下:

inline-block-demo.png

从效果图中可以看到列表 item 之间有一点小空隙,但是在代码中并没有设置 margin 水平间距。那么这个空隙是如何产生的呢?

这是因为编写代码时输入空格、换行都会产生空白符。而浏览器是不会忽略空白符的,且对于多个连续的空白符浏览器会自动将其合并成一个,故产生了所谓的间隙。

对于上面实例,在列表 item 元素之间输入了回车换行以方便阅读,而这间隙正是这个回车换行产生的空白符。

同样对于所有的行内元素(inline,inline-block),换行都会产生空白符的间隙。

如何消除空白符

从上面了解到空白符,是浏览器正常的表现行为。但是对于某些场景来说,并不美观,而且间隙大小非可控,所以我们需要去掉这个空白间隙。一般来说有两种方法来去掉这个换行引起间隙:代码不换行和设置 font-size。

代码不换行

由于换行空格导致产生换行符,因此可以将上述例子中的列表 item 写成一行,这样空白符便消失,间隙就不复存在了。其代码如下:

<div class="nav">
  <div class="nav-item">导航</div><div class="nav-item">导航</div><div class="nav-item">导航</div>
</div>

但考虑到代码可读及维护性,一般不建议连成一行的写法。

设置 font-size

首先要理解空白符归根结底是个字符,因此,可以通过设置 font-size 属性来控制产生的间隙的大小。如果将 font-size 设置为 0,文字字符是没法显示的,那么同样这个空白字也没了,间隙也就没了。

于是顺着这个思路就有了另一个解决方案:通过设置父元素的 font-size 为 0 来去掉这个间隙,然后重置子元素的 font-size,让其恢复子元素文字字符。

所以该方法代码如下:

.nav {
  background: #999;
  font-size: 0; /* 空白字符大小为0 */
}
.nav-item{
  display:inline-block;
  width: 100px;
  font-size: 16px; /* 重置 font-size 为16px*/
  background: #ddd;
}

使用该方法时需要特别注意其子元素一定要重置 font-size,不然很容易掉进坑里(文字显示不出来)。

对齐问题

由于 inline-block 属于行内级元素,所以 vertical-align 属性同样对其适用。

在正式讲解 vertical-align 之前,需要先说一些基本概念。

中线、基线、顶线、底线

中线(middle)、基线(baseline)、顶线(text-top、底线(text-bottom))是文本的几个基本线,其对应位置如下图:

inline-block-baseline.png

  • 基线(base line):小写英文字母x的下端沿。
  • 中线(middle line):小写英文字母x的中间。
  • 顶线(text-top):父元素 font-size 大小所组成的一个内容区域的顶部
  • 底线(text-bottom):父元素 font-size 大小所组成的一个内容区域的底部

vertical-align 的值

vertical-align 只接受8个关键字、一个百分数值或者一个长度值。下面将看看各关键字如何作用于行内元素。

描述
baseline默认元素的基线与父元素的基线对齐。
sub将元素的基线与其父元素的下标基线对齐。
super将元素的基线与其父代的上标 - 基线对齐。
text-top将元素的顶部与父元素的字体顶部对齐。
text-bottom将元素的底部与父元素的字体的底部对齐。
middle将元素的中间与基线对齐加上父元素的x-height的一半。
top将元素的顶部和其后代与整行的顶部对齐。
bottom将元素的底部和其后代与整行的底部对齐。
<length>将元素的基线对准给定长度高于其父元素的基线。
<percentage>像<长度>值,百分比是line-height属性的百分比。

元素浮动——float

设置元素的浮动。

三个取值: none(不浮动) | left(左浮动) | right (右浮动)

最开始的目的,是实现文字环绕图片的效果。

会引起父元素高度的塌陷。

清除浮动详解

清除浮动主要是为了解决由于浮动元素脱离文流导致的元素重叠或者父元素高度坍塌的问题,而这两个问题分别对应了需要清除浮动的两种种情况:清除前面兄弟元素浮动和闭合子元素浮动(解决父元素高度坍塌)。

清除前面兄弟元素浮动

清除前面兄弟元素浮动很简单,只需要在不想受到浮动元素影响的元素上使用 clear:both 即可, HTML & CSS 代码如下:

<div class="fl">我是左浮动元素</div>
<div class="fr">我是右浮动元素</div>
<div class="cb">我不受浮动元素的影响</div>
.fl {
    float: left;
}
.fr {
    float: right;
}
.cb {
    clear: both;
}

在 CSS2 以前,clear 的原理为自动增加元素的上外边距(margin-top)值,使之最后落在浮动元素的下面。在 CSS2.1 中引入了一个清除区域(clearance)——在元素上外边距之上增加的额外间距,使之最后落在浮动元素的下面。所以如果需要设置浮动元素与 clear 元素的间距,得设置浮动的元素的 margin-bottom,而不是 clear 元素的 margin-top。

demo 可见:clear 清除浮动

image.png

闭合子元素浮动

在计算页面排版的时候,如果没有设置父元素的高度,那么该父元素的高度是由他的子元素高度撑开的。但是如果子元素是设置了浮动,脱离了文档流,那么父元素计算高度的时候就会忽略该子元素,甚至当所有子元素都是浮动的时候,就会出现父元素高度为 0 的情况,这就是所谓的父元素高度坍塌问题。为了能让父元素正确包裹子元素的高度,不发生坍塌,需要闭合子元素的浮动。

一般有两种办法可以用来闭合子元素浮动:

  • 给最后一个元素设置 clear: both
  • 给父元素新建一个 BFC(块格式化上下文)

clear:both

由于最后一个元素使用 clear:both,所以该元素就能不受浮动元素影响出现在父元素的最底部,而父元素计算高度的时候需要考虑到这个正常元素的位置,所以高度自然包裹到了最底部,也就没有了坍塌。

对于这个方法,以前是利用新增一个空元素(<b> 或 <span> 或 <div> 等)来实现的,如下:

<div class="container">
    <div class="box"></div>
    <span class="clear-box"></span>
</div>
.box {
    float: left;
}
.clear-box {
    clear: both;
}

虽然这种办法比较直观,但是不是很优雅,因为增加了一个无用的空白标签,比较冗余而且不方便后期维护(一般不太建议使用该办法)。所以后期有了通过父元素的伪元素(::after)实现的著名 clearfix 方法,代码如下:

<div class="container clearfix">
    <div class="box"></div>
</div>
.clearfix::after {
    content:"";
    display:table;
    clear: both;
}

上面方法给父元素增加一个专门用于处理闭合子元素浮动的 clearfix 类名,该类使用 ::after 伪元素类选择器增加一个内容为空的结构来清除浮动,可能比较疑惑的是为什么要设置 display:table 属性,这其实涉及到一个比较复杂的进化过程。

新建 BFC

该方法的原理是:父元素在新建一个 BFC 时,其高度计算时会把浮动子元素的包进来。

下面以实例为证:如下图图片为浮动,父元素 article 的高度就出现了坍塌(没有包括图片),而根元素 HTML (默认情况下我们的根元素 HTML 就是一个 BFC)的高度则包括了图片的高度。

image.png

image.png

既然新建一个 BFC 可以解决父元素高度坍陷问题,那就好办了,下面这些都可以创建一个 BFC :

  • 根元素或其它包含它的元素
  • 浮动 (元素的 float 不是 none)
  • 绝对定位的元素 (元素具有 position 为 absolute 或 fixed)
  • 内联块 inline-blocks (元素具有 display: inline-block)
  • 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
  • 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
  • 块元素具有overflow ,且值不是 visible
  • display: flow-root

虽然有这么多方法可用,可常用的就是 overflow: hidden,代码如下:

<div class="container">
    <div class="box"></div>
</div
.container {
    overflow: hidden;
}
.box {
    float: left;
}

总结

上面主要讲解了比较常的一些清除浮动解决方案,看似简单的清除浮动方法其实则涉及到了很多复杂的CSS规则,在实际操作的时候可以针对不同的情况参考上面的方法。

float 布局

常见的诸如两栏布局,三栏布局。

image.png

image.png

#navsecond {
    float: left;
    width: 180px;
}
#maincontent {
    float: left;
    width: 740px;
}
#sidebar {
    float: left;
    width: 180px;
}

浮动布局——确定

image.png

宽度固定,1200px,主流宽度,适合 PC。

image.png

image.png

image.png

image.png

image.png

浮动布局——流体

image.png

会利用浏览器整个可视区域的宽度。

image.png

子元素:

image.png

左右边栏:

image.png

image.png

若这里不使用margin-left的负取值,会导致换行。

网格布局

image.png

例子:960 Grid System

image.png

image.png

image.png

元素位置——position

设定元素的定位方式

static(静态)| relative(相对)| absolute(绝对)| fixed(固定)

image.png

image.png

image.png

image.png

image.png

元素层级——z-index

元素不只是二维关系,它还有个三维上的层叠关系。

image.png

深入了解 z-index

网页正常文档流排版可以理解为在一个平面立面里面,元素占据空间,依次排列,互不覆盖。但是当页面中元素设置了定位属性的时候,难免会出现元素之间相互重叠的情况,比如下图小猫和小狗的图片都设置了绝对定位,2 张图片的位置重叠了,小猫图显示在小狗图上面。现在如果想要小狗图显示在上面,就需要涉及到 CSS 中的 z-index 属性了。

z-index-1.png

z-index

z-index 属性用于指定已定位元素在垂直于页面方向的排列顺序,其属性值有2种:auto(默认值)和整数。这里有2个需要注意的点:

  1. z-index 属性只对定位元素元素生效,也就是 position 属性不为 static 的元素。
  2. 除了默认值 auto, z-index 可以设置为任意整数,正数,0,负数都可以。

一般情况下,z-index 值进行比较有下面2条规则:

  1. 数值大的在上面(auto 数值上相当于0)。
  2. 数值相同的,在 HTML 结构中排后面的在上面。

所以上面例子,2张图都没设置 z-index 的值,在 dom 结构上排后面的小猫图展示在上面。如果想小狗图展示在小猫图上面,只需要设置小狗图的 z-index 属性值大于0就可以了。

<img class="dog" src="http://lala.com/img/p3/z-index/dog.png" alt="dog">
<img class="cat" src="http://lala.com/img/p3/z-index/cat.png" alt="cat">
.dog {
  position: absolute;
  top: 10px;
  left: 100px;
  z-index: 1; /* 设置小狗图的 z-index 值大于0 */
}
.cat {
  position: absolute;
  top: 80px;
  left: 70px;
}

给 .dog 元素增加了 z-index: 1 属性就可以让小狗图相比于小猫图在垂直于页面的方向上离使用者更近,这样效果就是小狗图显示在上面了。

z-index-2 (1).png

层叠上下文

上面说到,z-index 默认值 auto 数值上等于0,那设置了 z-index:0 和 默认的 z-index:auto; 有没有区别呢? 答案是有区别的。区别在于设置了 z-index 属性为整数值(包括0)的元素,自身会创建一个层叠上下文。而创建一个层叠上下文之后,其子元素的层叠顺序就相对于父元素计算,不会与外部元素比较。这样说比较抽象,来看个例子。

<div class="dog-container">
  <img class="dog" src="http://coding.imweb.io/img/p3/z-index/dog.png" alt="dog">
</div>
<div class="cat-container">
  <img class="cat" src="http://coding.imweb.io/img/p3/z-index/cat.png" alt="cat">
</div>
img {
  width: 200px;
}
.dog-container {
  width: 200px;
  height: 100px;
  background: red;
  position: relative;
  z-index: auto; /* 默认值auto */
}
.dog {
  position: absolute;
  top: 10px;
  left: 100px;
  z-index: 2;
}
.cat {
  position: absolute;
  top: 80px;
  left: 70px;
  z-index: 1;
}

上面例子中,给 .dog 和 .cat 增加了容器 .dog-container 和 .cat-container, 并且 .dog 和 .cat 都设置了 z-index 值,所以都显示在红色背景的 .container 之上,而且 .dog z-index 数值比较大,所以显示在上面。

z-index-3.png

但是当设置了 .dog-container 的 z-index 属性值为0之后,发现,z-index 值比较大的 .dog 元素反而到 z-index值比较小的 .cat 下面了

.dog-container {
  width: 200px;
  height: 100px;
  background: red;
  position: relative;
  z-index: 0; /* 将 z-index 值改成0 */
}

效果:

z-index-4.png

其原因就在于给 .dog-container 设置了 z-index:0 之后,.dog-container 就创建了自己的层叠上下文,其子元素 .dog 在比较层叠顺序的时候只会在 .dog-container 内比较,而不会与外面的 .cat 比较。如下图所示:

z-index-5.png

并不是所有情况 z-index 值大的元素都会在上面,在进行 z-index 比较的时候要留意其祖先元素有没有建立独立的层叠上下文,z-index 只有在同一个层叠上下文中比较才有意义。另外,对定位元素设置 z-index 属性不是唯一创建层叠上下文的方法,具有下面属性的元素都会创建层叠上下文(具体可参看:层叠上下文 | MDN):

  • 根元素 (HTML)
  • z-index 值不为 "auto"的 绝对/相对定位
  • 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex
  • opacity 属性值小于 1 的元素
  • transform 属性值不为 "none"的元素,
  • mix-blend-mode 属性值不为 "normal"的元素,
  • filter值不为“none”的元素,
  • perspective值不为“none”的元素,
  • isolation 属性被设置为 "isolate"的元素,
  • position: fixed
  • 在 will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值(参考这篇文章
  • -webkit-overflow-scrolling 属性被设置 "touch"的元素

也就是说,上面例子中, .dog-container 满足上面任意一条属性,也会一样出现上面的情况。比如设置 opacity 属性:

.dog-container {
  width: 200px;
  height: 100px;
  background: red;
  position: relative;
  opacity: 0.6; /* 设置 opacity 属性小于1也会创建层叠上下文 */
}

效果如下:

z-index-6.png

总结

  1. z-index 属性用于描述定位元素在垂直于页面方向上的排列顺序。
  2. z-index 一般比较规则是值大在上,值相同则排后面的在上。
  3. 元素在设置了某些属性的时候会创建层叠上下文,z-index 值比较大小只有在同一个层叠上下文才有效。

参考文档