Web

Web:CSS操作手册

选择器、属性、布局、Sass

Posted by BlackDn on August 16, 2022

“我是满船的烟波,你是海底的星河。”

Web:CSS 操作手册

前言

轮到 CSS 了,按照惯例给出前端学习路线
顺便给出一些闯关式教程:w3schoolsfreecodecamp
因为盒模型在之前的HTML里提到了所以这里就不说了
就是要注意一下布局排版的时候其原理是根据盒模型的block, inline 或者 inline-block

CSS

还是先简单介绍一下 CSS,层叠样式表(Cascading Style Sheet),是为网页添加样式的代码,不过注意他并不是编程语言
一个网页,即使不存在 CSS 也完全可以正常显示,因为 CSS 本身用的就是 HTML 中的元素,给这个元素设置属性,添加样式。
比如在 HTML 中,我们想把一个标题变为红色:

<h2 style="color: red;">I Like Cats</h2>

但是,随着属性的增多(背景色,字体,字号,边框…)和元素的增加(图片、标题、段落、链接…),这会导致 HTML 文件变得十分冗长难读
于是,聪明的程序员们把这一部分关于样式的代码分离出来,形成了 CSS。
我们想给index.html文件添加 CSS 样式,可以新建index.css文件,并在HTMLhead中添加如下代码:

<link href="./styles/index.css" type="text/css" rel="stylesheet" />

这样就绑定了 HTML 文件和他的 CSS 文件

CSS 选择器

既然我们分离出了 CSS 文件,那么 CSS 要怎样知道我修改的是哪一个元素呢?
于是就出现了CSS 选择器,即 CSS 中用于选定元素的语法:(点击可跳转到 MDN 文档)

选择器 示例 选择的内容
类型选择器 h1 { } 所有<h1>元素
通配选择器 * { } 全部元素
类选择器 .box { } 所有声明class="box"的元素
ID 选择器 #unique { } 所有声明id="unique"的元素
标签属性选择器 a[title] { } 所有存在 title 属性的<a> 元素
伪类选择器 button:hover { } 所有<button>元素的hover鼠标悬停样式
伪元素选择器 p::first-line { } 所有<p>元素的第一行文字
后代选择器 article p { } article元素中的所有<p>元素
子代选择器 article > p { } 仅被article元素包含的<p>元素
相邻兄弟选择器 h1 + p { } 紧跟在<h1>元素后的<p>元素(两者平级)
通用兄弟选择器 h1 ~ p { } 所有和<h1>同级的<p>元素

优先级:!important > ID > 类选择器 > 子代选择器
当一个属性声明!important之后,它将覆盖任何其他声明,即为最终显示。

p {
  color: red !important;
}

伪类和伪元素

上面的选择器中提到了伪类选择器和伪元素选择器,所以来介绍一下他们

伪类(Pseudo Classes) 是选择器的一种,它用于选择处于特定状态的元素。
比如button:hover帮助我们直接设置鼠标指针悬浮时按钮的样式、p:first-child帮助我们对多个兄弟元素p中的第一个元素设置样式。它的表现上就和我们自己设置的类一样,所以叫伪类。伪类允许我们对特定的元素或状态进行样式设置,从而减少多余的类,让代码更灵活、更易于维护。

常见的伪类:

伪类 作用
:first-child 一组兄弟元素中的首个元素
:last-child 一组兄弟元素中的最后元素
:only-child 没有任何兄弟元素的元素
:nth-child(n) 一组兄弟元素中的第 n 个元素
:nth-last-child(n) 一组兄弟元素中的倒数第 n 个元素
:nth-child(odd/even) 一组兄弟元素中的第奇数/偶数个元素
:invalid 选择任何未通过验证的 <form><input> 等表单元素。
:hover 将指针挪到元素上时激活
:focus 使用键盘控制,选定元素时激活

伪元素(Pseudo Elements) 和伪类相似,不过从表现形式上看,更像是加入了一个新的元素,而不是在现有的元素上应用类。伪元素开头为双冒号::
比如p::first-line能够设定元素p中的第一行文字的样式,这里的第一行是由页面展示的内容来决定的,当我们调整字体大小、元素宽度或屏幕宽度,都可能导致换行的位置改变,但p::first-line能够始终保持仅对第一行生效。
一个选择器中只能使用一个伪元素。伪元素必须紧跟在语句中的简单选择器/基础选择器之后。

常见的伪元素:

伪元素 作用
::after 在元素之前生成内容
::before 在元素之后生成内容
::first-line 元素的第一行内容
::file-selector-button type="file"<input> 的按钮。
::first-letter 块级元素(block)的第一行的第一个字母
::selection 被用户高亮的部分(如复制时选中的文本)
::marker 列表单个 item 前的标记框,通常设置给<li>
::placeholder 表单元素的占位文本

按照规范,为了方便区分,伪元素应该使用双冒号(::),伪类应该使用单冒号(:)。但是,由于旧版 W3C 规范并未强调这点,因此目前绝大多数浏览器也支持用单冒号来表示伪元素。

此外,伪类和伪元素可以组合实现。比如我有一个<article>元素,里面包含一组p元素,我想要第一段(第一个p元素)的第一行文本加醋,可以采用以下样式:

article p:first-child::first-line {
  font-weight: bold;
}

伪元素 ::before 和 ::after

这是一组特殊的伪元素,它们常和content属性一同使用,从而能够将 CSS 的内容插入到文档中。这种方式也被称之为生成内容
举个栗子 🌰:

<p class="box">This is the content.</p>

.box::after {
    content: "Hello World";
}

::after会将自己的content内容放到元素的后面,所以页面上显示的文本就是“This is the content.Hello World”,同理,::before就是放在前面
好消息是,这样做在文档(Inspect) 中,content中的内容不会被展示出来,从而可以隐藏一些内容、实现反爬虫等;
坏消息是,因为内容不会在代码层面展示,所以如屏幕阅读器等功能会失效,不利于无障碍设计,同时也不利于未来的查找、编辑和维护。

before in Insepct

但是更近一步讲,它们推荐的用法之一是插入一些视觉标志,如一些箭头、图标等,既能提高页面的视觉效果,也能让屏幕阅读器不读出它,提高无障碍设计体验。
此外,我们还能为其插入空字符串,然后用其他的属性来自定义我们的元素设计样式:

.box::before {
  content: "";
  display: block;
  width: 100px;
  height: 100px;
  background-color: rebeccapurple;
}

上面这段代码就实现了在box之前展示一个紫色的方块

常见属性

图片

/* 背景图片 */
background-image: url(../img/background-img.jpg);
/* 透明度 */
opacity: 0.75;

字体

/* 全大写, 首字母大写 */
text-transform: uppercase;
text-transform: capitalize;
/* 居中 */
text-align: center;
/* 字体 */
font-family: Arial, Helvetica, sans-serif;
/* 加粗 */
font-weight: bold;
/* 斜体 */
font-style: italic;
/* 字体颜色,背景颜色 */
color: black;
background-color: white;

链接

a:link {
  ...;
} /* 未访问过的链接 */
a:visite {
  ...;
} /* 已访问过的链接 */
a:hover {
  ...;
} /* 鼠标移到链接上的样式 */
a:active {
  ...;
} /* 鼠标在连接上按下时的样式 */
a:focus {
  ...;
} /* 获得焦点时的样式 */
/* 去掉下划线 */
text-decoration: none;

边框

border: 1px solid rgba(255, 255, 255, 0.5); /* 边框粗细、样式、颜色(透明度) */
border-radius: 3px; /* 边框圆角弧度 */

其他

/* 超出滚动 **/
overflow: scroll;
/* 隐藏 */
visibility: hidden;

Layout 布局

浮动布局 Float

这个布局依赖于元素的float属性,其包含三个值:

  • left — 将元素浮动到左侧。
  • right — 将元素浮动到右侧。
  • none — 默认值,不浮动。
  • inherit — 继承父元素的浮动属性。

默认情况下,HTML 会根据代码中元素的顺序从上到下布局,我们称其为正常布局流 (normal flow)
而当元素float设置为leftright时,这一元素会浮动到左侧或右侧,并且从正常布局流中移除,其他的内容就会在这个元素周围环绕
为了避免父元素高度坍塌而无法被撑开,最好给父元素添加以下属性:

fater-element:after {
  content: "";
  display: block;
  clear: both;
}

这里就不举例了,可以看看参考文档里的例子,都非常浅显易懂 hh

位置布局 Position

和浮动布局类似,位置布局依赖的属性是position属性,包含几个值:

  • static — 默认值。表示“将元素放在文档正常布局流的默认位置”,说了等于说好吧。
  • relative — 相对定位。允许我们相对于元素在正常的文档流中的位置移动它,即使会导致两个元素重叠。
  • absolute — 绝对定位。表示将元素从正常布局流中移出,类似将其单独放在一个图层中。使用toprigth(或bottomright)指定位置,参照对象是“离它最近的已定位的祖先元素”。
  • fixed — 固定定位。和绝对定位类似,但是它让元素相对浏览器窗口固定,而不是另外一个元素。比如某些页面右下角的回到顶部的按钮
  • sticky — 粘性定位。通常情况下其和static一样,当它的相对窗口位置 (offset from the viewport) 达到某一个预设值时(通过toprigth等设置),他就会像position: fixed一样定位。

还是懒得举例,知乎的这篇文章里的例子不错,有疑问的可以去看看:几种常见 CSS 布局
其中粘性定位较新提出,所以需要参考 MDN:粘性定位

表格布局 Table

在古早时期,许多浏览器还不支持 CSS 的时候,表格布局最为流行。
网页的页眉、页脚、不同的列等可以放在不同的表行和列中,这行之有效,却也伴随着许多问题——布局不灵活、形式单一,代码冗杂,难以调试等。

声明方法十分简单:display: table;
然后可以根据我们的想法来设置行和列,比如一个父元素声明display: table-row;,其内部的子元素声明display: table-cell;。这样就可以简单实现一行的布局。
此外,在表格中,可以通过声明以下属性来让元素内容显示在表格底部。

display: table-caption;
caption-side: bottom;

具体例子可见 MDN:表格布局

网格布局 Grid

网格布局比表格布局更新,通过声明display: grid来实现
在父元素(container)中,我们可以规定网格每行的列数、列宽度、列间隙等属性
而在子元素中,我们可以通过 grid-columngrid-row设置元素所占据的网格位置

.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 100px 100px;
  gap: 10px;
}

.box1 {
  grid-column: 2 / 4;
  grid-row: 1;
}

.box2 {
  grid-column: 1;
  grid-row: 1 / 3;
}

.box3 {
  grid-row: 2;
  grid-column: 3;
}

其中,fr(fraction)是 Grid 布局中新引入的单位,表示剩余空间(leftover space)的一部分(fraction),1fr表示100%的剩余空间。总之它设定了每个单元格的宽度,三个1fr则表示一行有三个单元格,并等分一行。
grid-column: 2 / 4;表示该元素占据位置从第二列开始,到第四列结束(不包括)。即占据 2、3 列。
此外,gap属性定义行与列之间的间隔,它原本是grid-gap属性,及grid-row-gapgrid-column-gap的组合属性,不过在 CSS3 中更名为gap
具体例子可见 MDN:Grid 布局

弹性布局 Flex

FlexFlexible Box,是另外一种布局方式。上述的布局方式依赖 display 属性 + position属性 + float属性
Flex则有着一套自己的属性和布局方式
相对而言功能性和可靠性都是Flex高一点,所以能用Flex的话还是推荐用Flex

在文档中,采用 Flex 布局的元素,称为 Flex 容器(flex container),即父元素;而其内部的子元素则称为 Flex 项目(flex item)。不过为了方便理解我就叫父元素和子元素了。
还是在文档中,水平方向称为主轴(main axis),垂直方向称为交叉轴(cross axis)

一般来说,任意一个元素都可以指定为Flex布局(通常我们把最外层的父元素指定为Flex),如果是行内Flex布局也可以指定inline-flex。当然Webkit内核的浏览器还要加上-webkit前缀。

.box {
  display: -webkit-flex; /* Safari */
  display: flex; /* 正常情况只用写这一行 */
  display: inline-flex; /* 如果是行内Flex就写这行 */
}

此外,设为 Flex 布局以后,子元素的floatclearvertical-align属性将失效。

父元素的属性

属性 作用
flex-direction 决定子元素的布局方向
row(默认):从左到右排列; row-reverse:从右到左排列; column:从上到下排列; column-reverse:从下到上排列
flex-wrap 决定如何进行换行
nowrap(默认):不换行;wrap:向下换行;wrap-reverse:向上换行
flex-flow flex-direction属性和flex-wrap属性的简写形式。
默认为:flex-flow: row nowrap;
justify-content 决定子元素在布局方向上如何对齐
flex-start(默认):左对齐;flex-end:右对齐;center:居中;space-between:两端对齐,子元素间隔相等;space-around:每个元素的间隔相等(类似于每个元素有相同的margin值)
align-items 决定子元素在非布局方向上如何对齐
flex-start:起点对齐(水平排列就上方对其,竖直排列就左边对其);flex-end:终点对齐;center:居中对齐;baseline: 第一行文字的基线对齐;stretch(默认):未设置高度或设为 auto,将占满整个父容器
align-content 决定子元素多行的对齐方式
flex-start:非布局方向的起点对齐;flex-end:非布局方向的终点对齐;center:在非布局方向上居中; space-between:在非布局方向上,多行间隔平均分布;space-around:每行的间隔都相等,所以中间行的间隔会大一倍;stretch(默认):填满整个非布局方向

可能看的会有点绕,可以结合Flex 布局教程:语法篇里面的图片理解

子元素的属性

属性 作用
order 定义子元素的排列顺序。数值越小,排列越靠前,默认为0
flex-grow 定义子元素的放大比例,默认为0。若全为1则会等分剩余空间
flex-shrink 定义子元素的缩小比例,默认为1,表示空间不足时缩小该元素
flex-basis 定义在分配多余空间前子元素占据的空间大小,默认为auto,即元素自身的大小。若设为跟widthheight一样的值(如 350px),则元素将占据固定空间大小。
flex flex-grow, flex-shrinkflex-basis的简写,默认为0 1 auto。若值为auto表示1 1 auto,值为none表示0 0 auto
align-self 定义当前子元素的对齐方式,可覆盖align-items。默认为auto,表示继承父元素的align-items。其余可选参数和align-items一致

Sass

在使用 CSS 的过程中,人们开始觉得 CSS 也开始变得有些麻烦了,比如它不能使用变量,修改一些颜色要全部改一遍;比如用选择器定位元素的时候,共同的父元素要重复写等等。
那要怎么办呢,如今浏览器都适配 CSS 了,也不能提出一个全新的语言。
于是聪明的人们想啊,我可以先用一个全新的规则编写样式,写完后把这个文件转变成 CSS 文件不就好了。
于是就提出了 Sass (Syntactically Awesome Stylesheets),它是一个 CSS 预处理器,并兼容所有版本的 CSS。HTML 网页是不知道 Sass 是什么的,因此编写完 Sass 后,需要将其转为 CSS。
VS Code中,插件Live Sass Compiler可以实时将 Sass 转为 CSS,我们每次修改 Sass 后都可以看到 CSS 里有哪些变化。

严格来说 Sass 有着两个版本,后缀名分别为.sass.scss,两种语法各有差别,比如.sass移除了括号{}和分号;,而.scss则保留了它们。相对而言,.scss更加流行普遍,因此下面都用这种版本。
Sass 语法大体和 CSS 类似(直接在里面写 CSS 也不会错),这里主要介绍一下它的一些新特性

变量

我们可以用$定义变量,方便后续使用 :

$font-family-base: "Open Sans", sans-serif !default;
$white: #fff !default;
p {
  color: $white;
  font-family: $font-family-base;
}

其中!default是用于为变量提供默认值,避免 Sass 的初始化失败
还要注意一点,Sass 命名时不区分横杠-和下划线_,也就是说$font-family-base$font_family_base是一样的。

特别是一些颜色,很多网站有着自己的主色调,如果想要修改就非常麻烦,需要把每个元素的颜色都进行修改。有了变量之后,就可以实现“修改一行,全部修改”了。

嵌套

Sass 的嵌套规则有效地帮我们省略了元素共同的父元素,可以直接在括号中进行子元素的属性设定。
比如我的nav有 li 和button两个元素,我可以按照如下方式定义他们的属性:

nav {
  li {
    display: inline-block;
    margin-bottom: 8px;
    &:last-child {
      margin-bottom: 0;
    }
  }
  button {
    background-color: $white;
    &:hover {
      text-decration: none;
    }
  }
}

在定义元素的时候,&就表示其父元素,因此可以用于链接一些特殊属性,比如&:last-child表示列表最后一个元素,实现去掉最后一个元素的下划线;&:hover就表示光标悬停在button上的状态。

@mixin 定义样式

我们可以定义变量,自然也可以定义样式,比如文本啊按钮啊啥的,很多时候需要有统一的样式。
我们可以用@mixin定义一个样式,然后在想用的时候用@include引入

@mixin important-text {
  color: red;
  font-size: 25px;
  font-weight: bold;
  border: 1px solid blue;
}

.main-text {
  @include important-text;
  background-color: green;
}

还有更高级的用法,可以为样式设置变量并使用
比如给button设置文字颜色和背景颜色的变量:

@mixin colored-button($color, $bg) {
  color: $color;
  background: $bg;
}
.orange-btn {
  @include colored-button(black, orange);
}

使用语句块

我们可以使用if-else语句,比如根据$theme主题来选择显示的背景色:

@mixin theme-colors($theme) {
  @if $theme == "light" {
    background-color: $light-bg;
  } @else {
    background-color: $dark-bg;
  }
}

还可以用循环来为不同元素分别设置属性
比如给classtext-40pxtext-50pxtext-60px分别设置$sizes里的字号:

$sizes: 40px, 50px, 60px;
@each $size in $sizes {
  .text-#{size} {
    font-size: $size;
  }
}

函数

不仅如此,Sass 还引入了函数。
它有许多内置函数,比如用lighten()darken()可以快速调整颜色亮度。

$primary-color: green;
.button {
  background: lighten($primary-color, 25%);
  color: darken($primary-color, 25%);
}

我们还可以自定义函数
这是一个将$numbers里所有值求和的函数:

@function sum($numbers) {
  $sum: 0;
  @each $bumber in $numbers {
    $sum: $sum + $number;
  }
  @return $sum;
}

@extend 继承样式

如果一个样式与另外一个样式几乎相同,只有少量的区别,那么我们可以把相同的内容抽离,在使用的时候用 @extend导入,而那些不同的样式再进行额外设置。
比如有两个很类似的按钮,一个用于报告report,一个用于提交submit,他们只是颜色不一样。因此文字、边框等相同的东西我们就可以抽离出来,再继承之后继续设置样式:

.button-basic {
  border: none;
  padding: 15px 30px;
  text-align: center;
  font-size: 16px;
}

.button-report {
  @extend .button-basic;
  background-color: red;
}

.button-submit {
  @extend .button-basic;
  background-color: green;
  color: white;
}

@import 导入 Sass

当我们写了很多代码后,难免会想着想把相同内容的代码分离,提高可读性和简洁性。
比如我们可以将全局的样式提取,作为reset.scss文件:

//reset.scss
html,
body {
  margin: 0;
  padding: 0;
}

于是,在其他 Sass 文件中我们就可以用@import 导入
导入的时候只用写文件名,不用写后缀,Sass 会自动帮我们添加后缀。

//index.scss
@import "reset";

header {
  //······
}
main {
  //······
}

不过这样的坏处是 Sass 会给reset.scss也生成一个对应的 CSS 文件。
如果不想将一个 Sass 的代码文件编译成 CSS 文件,我们可以在文件名的开头添加一个下划线_
比如我们把变量全部提取出来,作为_variables.scss文件

//_variables.scss
$font-family-base: Arial, Helvetica, sans-serif;
$line-height-base: 1.5;

$white: #fff;
$gray: #a9a9a9;
$black: #000;

这样_variables.scss就不会生成一个 CSS 文件了(显然生成了也没必要)
在导入的时候,我们不需要添加前面的下划线

//index.scss
@import "variables";

header {
  //······
}
main {
  //······
}

不过要注意,带下划线与不带下划线的同名文件不要放置在同一个目录下,比否则带下划线的文件将会被忽略。毕竟大家用@import导入的时候都是一样的名字。

参考

  1. 前端学习路线
  2. MDN:CSS 基础 / w3schools:CSS Tutorial
  3. 伪类和伪元素
  4. MDN:CSS 布局
  5. 几种常见 CSS 布局
  6. Flex 布局教程:语法篇
  7. 菜鸟教程:Sass 教程
  8. Sass 中文文档
  9. Sass in 100 Seconds