第 3 讲 · 用户界面软件课程阅读笔记

这页整理的是 IN4MATX 133 第 3 讲 的课程笔记,适合按讲复习 HTML、CSS、JavaScript、DOM 和 UI 工具,并配有可编辑运行的实时演示。下面这条路径可以带你回到 Notes、Course Notes、课程主页,或定位到当前笔记。将鼠标悬停在右侧的 ≡ 图标上可以打开目录。

最后编辑于 2026 年 10 月 17 日

IN4MATX 133: User Interface Software — Lecture 3:CSS

目录

UCI IN4MATX 133: User Interface Software — Prof. Travis Linden Reid,2026 年秋季学期,10 月 1 日(周四)
内容整理自 Lecture 3 的幻灯片(共 65 页,包括 p57–65 的“Additional slides”)以及 demo 仓库文件夹 W1_L2_Oct1_CSS。按概念组织,而不是按来源组织。

📅 课程安排:Office hours 本周开始。周五的 Discussion section 是线上的(Zoom 链接在 Canvas 上)。
A1 的截止时间改为 10 月 12 日(周一)午夜——听完这节课,你已经掌握了完成它所需的内容。
之后所有作业都是每隔一周的周一午夜截止:A1 – 第 3 周,A2 – 第 5 周,A3 – 第 7 周,A4 – 第 9 周,A5 – Finals 周。


1. CSS 是什么,为什么要与 HTML 分开

CSS(Cascading Style Sheets,层叠样式表)定义页面的样式规则。这与 HTML 不同,HTML 提供的是文档的结构。开场的例子(幻灯片 4)是同一个 HTML 页面配上三套不同的样式表:朴素的白底页面、黑色“终端”风格,以及带按钮侧边栏的红色页面——内容相同,外观完全不同。幻灯片 7–8 在浏览器 DevTools 里展示了同一个页面:左侧的元素树(div.container、#sidebar 等)在两种情况下完全一致,只有 #sidebar 上的 CSS 规则(颜色、position、padding)不同。

为什么要把样式放在单独的文件里(幻灯片 9):

好处 含义
可复用(Reusability) 同一套样式可以应用到多个网页
模块化(Modularity) 一个页面可以引入多个样式表
管理清晰(Sane management) 样式表可以与 HTML 的结构内容分开做版本控制
易维护(Maintainability) 更容易找到页面样式所在的位置

HTML 规定语义,CSS 规定外观(幻灯片 20–21)。比较两种写“强调”的方式:

<!--HTML-->
<p> This text is
<i>emphasized!</i></p>

<!--HTML-->
<p> This text is
<em>emphasized!</em></p>

<i> 把外观和语义混在了一起(它的意思就是“斜体”)。<em> 对外观只字不提——它表示“这里需要强调”,浏览器的默认样式恰好把它画成斜体。如果想让强调看起来不一样,应该改 CSS,而不是改标签。

🔗 延伸阅读:W3Schools:同一页面,不同样式表(demo)

2. CSS 语法、引入样式表与默认样式

2.1 规则:选择器 + 属性

一条 CSS 规则(rule)由一个选择器(selector)(规则作用于哪些元素)加上一个由 property: value 声明组成的代码块(要设置什么格式)构成。一条规则可以包含多个属性:

/* CSS Pseudocode */
selector {
    property: value;
    property: value;
    ...
}

课上的第一个例子(来自 cssBasics.css)给所有 h1 设置样式:

h1 {
	font-family: 'Arial';
	color: blue;
	background-color: #ff0000; /*red*/
}

h1 是选择器(“应用于所有 h1 标签”);font-family 就是大多数人说的“字体”;/* … */ 是 CSS 中唯一的注释语法。

2.2 在 HTML 中引入样式表

在 HTML 的 <head> 中链接样式表:

<head>
    <link rel="stylesheet" href="my-style.css">
</head>

rel 表示当前页面与被引用文件之间的关系(relation)。<link> 没有内容,所以没有结束标签。cssBasics.html 就是这样引入 cssBasics.css 的;只有标题应用了样式——段落不受影响,因为规则的选择器是 h1。

在下面试一试——修改 CSS 然后点击 Run:

{"title": "Slide 12 · cssBasics.html + cssBasics.css", "html": "    <h1>This is a header!</h1>\n    <p>This is a paragraph. Notice how it doesn't have the header styles applied.</p>", "css": "h1 {\n\tfont-family: 'Arial';\n\tcolor: blue;\n\tbackground-color: #ff0000; /*red*/\n}", "height": 150, "tab": "css", "interactive": true}

🧪 Demo 文件:cssBasics.html · cssBasics.css

2.3 为什么 h1 这么大?默认值

我们从没设置过大小,标题却很大:CSS 有默认值。浏览器的默认样式给 h1 设置了 font-size: 2em——默认 16px 的两倍,所以标题渲染为 32px。

2.4 放置 CSS 的三个位置(Additional slides,课上未讲)

位置 示例 评价
行内(inline,style 属性) <p style="...">Red text</p> 支持,但通常是不好的做法:在每个元素上重复同样的样式违背 DRY(Don't Repeat Yourself)原则
内部(internal,<head> 中的 <style>) <style type="text/css"> p {font-family:'Arial'; color:red;} </style> 直接把 CSS 写进 HTML 的 <head>
外部(external,用 <link> 链接 .css 文件) <link rel="stylesheet" href="./css/style.css"> 通常是最佳实践——把结构和样式分开

幻灯片 58 上的行内示例写成了 style="font-family='Arial'; color=red;"。这是幻灯片上的错误:在 style 里面写的仍然是 CSS,要用冒号,所以应该是 style="font-family:'Arial'; color:red;"。写成 = 的版本会被忽略,文字仍然是黑色的 Times 字体。

🔗 延伸阅读:W3Schools:CSS 默认值

3. 元素、类与 ID 选择器

三种基本选择器(幻灯片 14),这里它们的声明完全相同:

p {                 /* element:要设置样式的标签 */
    font-family: 'Arial';
    color: red;
}
.emphasize {        /* class:一类元素 */
    font-family: 'Arial';
    color: red;
}
#redtext {          /* id:某一个特定元素 */
    font-family: 'Arial';
    color: red;
}

. 表示 class,# 表示 id,没有前缀表示标签名。class 和 id 就是带值的普通 HTML 属性,和 href、src 一样(幻灯片 15):

<div class="widget foo" id="baz"></div>
  • 一个元素可以有多个 class(用空格分隔),但只能有一个 ID。
  • 每个页面中,同一个 ID 只能出现在一个元素上——这是通过验证的要求。
  • 同一个 class 可以(也应该)用在很多元素上;这就是一次给很多东西设置样式的方法。

选择器可以链式连接(中间不加空格),要求同时满足所有条件(幻灯片 16):

div.widget.foo#baz {
    /*can chain selectors together!*/
}

这会匹配同时具有 class widget、class foo 并且 id 为 baz 的 div。(加上空格则含义不同——见后代选择器。)

小技巧(幻灯片 17):ID 可以用于页面内导航。http://example.com/#id 会直接滚动到具有该 id 的元素——幻灯片展示了一份课程大纲跳转到它的“Electronics”部分。

一步步看这些选择器(颜色是我自己加的,方便看清每条规则):

{"title": "Slides 14–16 · element, class, ID and chained selectors (colours are mine, to tell the rules apart)", "html": "<p>A plain paragraph</p>\n<p class=\"emphasize\">A paragraph with class=\"emphasize\"</p>\n<p id=\"redtext\">A paragraph with id=\"redtext\"</p>\n<div class=\"widget foo\" id=\"baz\">div class=\"widget foo\" id=\"baz\"</div>\n<div class=\"widget\">div class=\"widget\" only</div>", "css": "body { font-family: 'Arial'; }", "steps": [{"note": "Element selector p: every <p> on the page.", "css": "p {\n  color: red;\n}"}, {"note": "Class selector .emphasize: only elements with that class.", "css": ".emphasize {\n  background-color: #fde68a;\n}"}, {"note": "ID selector #redtext: the one element with that id.", "css": "#redtext {\n  border: 2px solid red;\n}"}, {"note": "Chained div.widget.foo#baz: a div with BOTH classes AND that id, so the plain .widget div is not matched.", "css": "div.widget.foo#baz {\n  background-color: #bfdbfe;\n}"}], "height": 200, "interactive": true}

⚠️ 易错点:#name 匹配 id,.name 匹配 class,而单独的 name 匹配的是标签。本来想选 <em> 标签却写成 #em 或 .em,或者本来想选 id="zot" 却写成 zot,都不会匹配到任何东西——这正是 Question 1 里的陷阱。

📝 练习:Question 1:哪段 CSS 能渲染出 ZOT! 段落?

🔗 延伸阅读:CSS-Tricks:ID 与 class 的区别

4. 常用属性:给课程表设置样式

你会经常用到的属性(幻灯片 18):

属性 控制什么
font-family “字体”(备选字体用逗号分隔——见 §10)
font-size 文字大小
font-weight 粗细(bold,或 700 这样的数字)
color 文字颜色
background-color 元素的背景
opacity 透明度

……还有很多很多(见参考链接)。

Demo 1(幻灯片 19,“Let's style a class schedule”)正是把这些属性应用到一张表格上。setup1.html 是一张 5 天的表格,单元格里放着 <div class="discussion">、<div class="lecture">,以及一个 <div id="a1">Assignment due</div>;setup1.css 里只有 TODO 清单。解答用到了一个标签选择器、两个类选择器和一个 ID 选择器:

th {
	font-size: 150%;
}

.discussion {
	background-color: limegreen;
}

.lecture {
	background-color: royalblue;
	color: white;
}

#a1 {
	background-color: saddlebrown;
	color: white;
}

一条规则一步地看解答:

{"title": "Slide 19 · Demo 1: setup1.html → solution1.css, one rule per step", "html": "   <h1>My 133 Schedule</h1>\n   <table>\n   \t<tr>\n   \t\t<th>Monday</th>\n   \t\t<th>Tuesday</th>\n   \t\t<th>Wednesday</th>\n   \t\t<th>Thursday</th>\n   \t\t<th>Friday</th>\n   \t</tr>\n   \t<tr>\n   \t\t<td>\n   \t\t\t<div class=\"discussion\">Discussion</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t</tr>\n   \t<tr>\n   \t\t<td>\n   \t\t\t<div class=\"discussion\">Discussion</div>\n   \t\t\t<div id=\"a1\">Assignment due</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t</tr>\n   </table>", "css": "", "steps": [{"note": "th { font-size: 150%; } — header row bigger.", "css": "th {\n\tfont-size: 150%;\n}"}, {"note": ".discussion — limegreen background.", "css": ".discussion {\n\tbackground-color: limegreen;\n}"}, {"note": ".lecture — royalblue background, white text.", "css": ".lecture {\n\tbackground-color: royalblue;\n\tcolor: white;\n}"}, {"note": "#a1 — saddlebrown background, white text.", "css": "#a1 {\n\tbackground-color: saddlebrown;\n\tcolor: white;\n}"}], "height": 230}

🧪 Demo 文件:setup1.html · setup1.css · solution1.css

📝 练习:Demo 1:给课程表上色

🔗 延伸阅读:W3Schools:CSS 参考

5. 层叠:多条规则、继承、顺序与优先级

“Cascading(层叠)”就是 CSS 里的 C:多条规则可以同时作用于同一个元素,浏览器会把它们合并起来。

多条规则(幻灯片 28)。一个带有两个 class 的元素会同时获得标签规则和两个 class 的规则:

<!--HTML-->
<p class="big blue">
This tag has two classes: "big" and "blue"
(classes are separated by spaces)
</p>
/* CSS */
p { font-family: 'Verdana'; }   /* apply to all <p> tags */
.big { font-size: larger; }     /* apply to all with class="big" */
.blue { color: blue; }          /* apply to all with class="blue" */

结果:Verdana 字体、更大(19.2px,而不是 16px)、蓝色。

继承(Inheritance)(幻灯片 29)。许多 CSS 属性(字体、颜色)还会从父标签继承,所以深层嵌套的元素会从它所有的祖先那里收集样式:

<div class="content"> <!-- has own styling -->
  <div class="sub-div"> <!-- has own styling + .content styling -->
    <ol class="my-list"> <!-- own styling + .sub-div + .content -->
                         <!-- own style is ol AND .my-list rules-->
      <!-- li styling + .my-list + .sub-div + .content -->
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ol>
  </div>
</div>

顺序(Order)(幻灯片 30)。规则按顺序应用:在同级(peer)选择器之间,后面的规则获胜。

<!--HTML-->
<p class="red green">
<em class="blue">Text is blue!</em>
</p>
/* CSS */
p { font-family: 'Verdana'; }
.red { color: red; }
.green { color: green; }
.blue { color: blue; }

.red 和 .green 都是匹配该 <p> 的类选择器,所以 CSS 中写在后面的(.green)获胜——<p> 是绿色的。class 属性里的顺序无关紧要:class="green red" 仍然是绿色。<em> 是蓝色,因为直接作用于该元素本身的规则(.blue)胜过它只是从父元素继承来的颜色。

外部样式表同样按顺序应用(additional slide 61)。对于

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

style.css 在 bootstrap.css 之后加载,所以两者冲突时你的规则会覆盖 Bootstrap 的。把自己的样式表放在最后链接。

优先级(Specificity)(幻灯片 31)。当选择器不是同级时,CSS 会根据选择器的类型计算优先级。一般规则是:“离 HTML 元素更近”的规则生效。从最具体到最不具体:

更具体 → 更不具体
行内 style 属性 ID 选择器(#banner) 类、伪类、其他属性(.sidebar1) 元素、伪元素(ul、h1)

幻灯片也承认这部分很难——通常靠反复试验(以及 DevTools,见 §11)就能解决大多数问题。

{"title": "Slides 28 & 30 · several rules on one element, last peer rule wins", "html": "<p class=\"big blue\">\nThis tag has two classes: \"big\" and \"blue\"\n(classes are separated by spaces)\n</p>\n<p class=\"red green\">\n<em class=\"blue\">Text is blue!</em> (and this part of the p?)\n</p>", "css": "p { font-family: 'Verdana'; }\n.big { font-size: larger; }\n.blue { color: blue; }", "js": "for (const el of document.querySelectorAll('p, em')) {\n  const c = getComputedStyle(el);\n  console.log(el.tagName, '\"' + el.className + '\"', c.color, c.fontSize);\n}", "steps": [{"note": "Add .red then .green (slide 30): both match the second <p>; .green comes later, so green wins. The <em> has its own .blue rule, which beats the colour it would inherit.", "css": ".red { color: red; }\n.green { color: green; }"}], "height": 150, "interactive": true}

⚠️ 易错点:“后面的规则获胜”只能在相同优先级的选择器之间分出胜负。先写的 #id 规则仍然胜过后写的 .class 规则。

6. Block 与 inline,以及盒模型

到目前为止,页面就是“一长串列表”(幻灯片 32):文字一行接一行,就像用不同字体的打字机。要缩进、分栏、让各部分重叠或加边框,我们就需要对元素进行定位。

6.1 Block 与 inline 元素

HTML 标签(几乎)都属于以下两类之一:

  • Block(块级)元素——另起一行,后面有换行(<p>、<div>、<h1>–<h6>、<ul>、<ol>、<li>、<table>、<form>、<header>、<footer>、<nav>、<section>、<article> 等)
  • Inline(行内)元素——没有换行,在文字中顺流排布(<a>、<em>、<strong>、<span>、<img>、<b>、<i>、<code>、<input>、<button>、<label> 等)
<p>
This is on a line.
<em>This is on the same line.</em>
</p>
<p>This will be on a new line.</p>

<p> 是 block,<em> 是 inline。不要把 block 元素放进 inline 元素里面!(幻灯片 34 列出了 W3Schools 上的全部元素。)

6.2 盒模型(box model)

每个元素都是一个由四层构成的盒子,从内到外依次是:

层 作用
Content(内容) “真正的”内容;其大小由 width 和 height 决定
Padding(内边距) 扩展内容区域;背景色会延伸到这一层
Border(边框) 类似,绘制在 padding 外围;有 border-color、border-width、border-style
Margin(外边距) 透明的空白,把元素与相邻元素隔开

padding、border 和 margin 都有按方向的属性:padding-top、margin-right、border-left 等。

所有定位都是相对于父元素的:如果标签嵌套,子元素的 margin 等是在父元素的盒子内部测量的。

Demo 2(幻灯片 38)接着做课程表。setup2.css 中的 TODO:给所有单元格加 1 像素的实线边框,给表头单元格加 5px 的 padding,给每个 discussion、lecture 和 assignment 加 5px 的 margin。解答:

td, th {
	border-width: 1px;
	border-style: solid;
}

th {
	font-size: 150%;
	padding: 5px;
}

.discussion {
	background-color: limegreen;
	margin: 5px;
}

.lecture {
	background-color: royalblue;
	color: white;
	margin: 5px;
}

#a1 {
	background-color: saddlebrown;
	color: white;
	margin: 5px;
}

带逗号的 td, th 是选择器列表(selector list):规则同时应用于两者。现在每个单元格都有自己的盒子;彩色方块与单元格边缘之间有 5px 的距离,表头文字在边框内有 5px 的 padding。

{"title": "Slide 38 · Demo 2: setup2.css → solution2.css (border, padding, margin)", "html": "   <h1>My 133 Schedule</h1>\n   <table>\n   \t<tr>\n   \t\t<th>Monday</th>\n   \t\t<th>Tuesday</th>\n   \t\t<th>Wednesday</th>\n   \t\t<th>Thursday</th>\n   \t\t<th>Friday</th>\n   \t</tr>\n   \t<tr>\n   \t\t<td>\n   \t\t\t<div class=\"discussion\">Discussion</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t</tr>\n   \t<tr>\n   \t\t<td>\n   \t\t\t<div class=\"discussion\">Discussion</div>\n   \t\t\t<div id=\"a1\">Assignment due</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t\t<td>\n   \t\t\t<div class=\"lecture\">Lecture</div>\n   \t\t</td>\n   \t\t<td>\n   \t\t</td>\n   \t</tr>\n   </table>", "css": "th {\n\tfont-size: 150%;\n}\n\n.discussion {\n\tbackground-color: limegreen;\n}\n\n.lecture {\n\tbackground-color: royalblue;\n\tcolor: white;\n}\n\n#a1 {\n\tbackground-color: saddlebrown;\n\tcolor: white;\n}", "steps": [{"note": "1-pixel solid border on all cells.", "css": "td, th {\n\tborder-width: 1px;\n\tborder-style: solid;\n}"}, {"note": "5px padding inside the header cells.", "css": "th {\n\tpadding: 5px;\n}"}, {"note": "5px margin around every discussion, lecture and assignment box.", "css": ".discussion, .lecture, #a1 {\n\tmargin: 5px;\n}"}], "height": 240, "interactive": true}

🧪 Demo 文件:setup2.html · setup2.css · solution2.css

📝 练习:Demo 2:边框、padding 与 margin

6.3 简写(Additional slides,课上未讲)

padding/margin 可以在一行里写 1–4 个值,从顶部开始顺时针方向(“TRouBLe”:Top、Right、Bottom、Left):

写法 含义
padding: 10px 5px 5px 10px; 上 10,右 5,下 5,左 10
padding: 10px 5px 10px; 上 10,左和右 5,下 10
padding: 8px 12px; 上和下 8,左和右 12

很难记住;写得明确一些会以牺牲简洁为代价提高可读性。border 的简写更容易——下面两种写法是等价的:

div {
    border-bottom-width: 3px;
    border-bottom-style: solid;
    border-bottom-color: red;
}

div.equivalent {
    border-bottom: 3px solid red;
}

⚠️ 易错点:边框需要 border-style。只写 border-width: 1px 什么也画不出来——这就是 solution2.css 两个都设置的原因。

🔗 延伸阅读:W3Schools:HTML block 与 inline 元素 · MDN:CSS 盒模型简介 · W3Schools:CSS 盒模型 · CSS-Tricks:用 “TRouBLe” 记住 margin/padding 简写顺序 · W3Schools:CSS 边框

7. Position 类型

position 属性有四个值(幻灯片 39):

值 行为 遵循页面流(flow)?
static 默认值 是
relative 相对于它原本所在位置偏移 是——有助于对页面流做调整
absolute 从(最近的已定位祖先元素的)左上角开始放置 否——完全忽略页面流
fixed 类似 absolute,但是浮动的:页面滚动时位置不变 否

absolute 和 fixed 会忽略页面流,但在某些情况下很有用(幻灯片 40):用 relative 定位把一个“member? SIGN IN ABOVE”徽章挪到表单的角落,以及用 absolute 定位放置一个悬浮操作按钮(floating action button,FAB)——邮件 app 角落里那个圆形的“+”。

下面的例子是我自己写的(仓库里没有这些幻灯片对应的文件):一个用 relative 挪动的盒子,以及一个在 relative 卡片内部用 absolute 放置的“+”按钮,也就是幻灯片中 CSS-Tricks 链接里的模式。

{"title": "Slides 39–40 · static, relative, and absolute inside relative (my example)", "html": "<div class=\"card\">\n  <div class=\"box\">static</div>\n  <div class=\"box rel\">relative</div>\n  <div class=\"box\">static</div>\n  <button class=\"fab\">+</button>\n</div>", "css": ".card { border: 2px solid #444; padding: 10px; height: 150px; }\n.box { display: inline-block; width: 80px; padding: 8px; background: #bfdbfe; }\n.fab { width: 40px; height: 40px; border-radius: 50%; }", "steps": [{"note": "position: relative with top/left nudges the box from where it would be; its old spot stays reserved.", "css": ".rel { position: relative; top: 20px; left: 10px; background: #fca5a5; }"}, {"note": "position: absolute on the button takes it out of the flow; with a relative parent, bottom/right are measured from the card's corner (a floating action button).", "css": ".card { position: relative; }\n.fab { position: absolute; bottom: 10px; right: 10px; }"}], "height": 200, "interactive": true}

🔗 延伸阅读:Interneting Is Hard:高级定位 · CSS-Tricks:relative 内部的 absolute(示例)

8. 单位

CSS 长度单位有像素(px)、元素单位(em)、百分比(%)和现实世界单位(in、cm)。

  • 尽可能使用相对单位(em、%)。这有助于无障碍访问:低视力的人会修改浏览器的默认文字大小(通常是 16px)。em 的大小会基于这个默认值缩放;而固定为 30px 的标题无论用户怎么设置都还是 30px。
  • 相对单位还可以根据屏幕大小改变布局——下一讲会详细介绍(Lecture 4)。

幻灯片上关于在什么场景用哪些单位的表:

推荐 偶尔使用 不推荐
屏幕 em、px、% ex pt、cm、mm、in、pc
打印 em、cm、mm、in、pt、pc、% px、ex

本讲中的例子:Demo 1 里的 th { font-size: 150%; } 是相对的——继承大小的 1.5 倍——而默认的 h1 是 2em。

🔗 延伸阅读:Engage Interactive:em、rem 与 px 的对比

9. 高级选择器与伪类

组合器(combinator)根据元素在树中的位置来选择元素。它们“对写出整洁的样式表极其有用”——你按结构来设置样式,而不必给每个东西都加一个 class。

选择器 名称 选中
ul li(空格) 后代(Descendant) ul 里的所有 li——子元素和孙元素
ul > li 子(Child) 只选 ul 的直接子元素 li(不含孙元素)
p + h2 相邻兄弟(Adjacent sibling) 紧跟在 p 兄弟元素之后的 h2
p ~ h2 通用兄弟(General sibling) p 兄弟元素之后任意位置的 h2(同一个父元素)

幻灯片 42 的例子:给所有跟在段落后面的 h2 加上外边距顶部,或者只对某个特定 div 里的这类 h2 加:

p + h2 {
    margin-top: 10px;
}

div.post p + h2 {
    margin-top: 10px;
}

第二条把所有东西组合了起来:一对 p + h2,并且它是 class 为 post 的 div 的后代。

幻灯片 43–45 用小型的树来说明。在 <ul><li>one</li><li>two<ol><li>nested one</li><li>nested two</li></ol></li><li>three</li></ul> 中,ul li 命中全部五个 li,ul > li 只命中 one、two、three。在 <div><p>Line One</p><p>Line Two</p><div>Box</div><p>Line Three</p></div> 中:p + p 只命中 Line Two;div + p 命中 Line Three;p ~ p 命中 Line Two 和 Line Three;div ~ p 命中 Line Three。

advancedSelectors.css(来自仓库),应用于一个包含标题、段落和 Reid 教授家猫的药物清单的页面:

h1 {
    margin-top: 0px;
}

h2 {
    margin-top: 0px;
}

p ~ h2 {
    color: blue;
}

p + h1 {
	margin-top: 10px;
}

p + h2 {
	margin-top: 10px;
}

ul > li {
    font-weight: bold;
}

你会看到:紧跟在段落后面的两个 h1 重新获得了 10px 的上外边距;紧跟在另一个 h1 后面的 h1 以及第一个 h1 保持为 0。唯一的 h2 前面(同一个 <body> 中更早的位置)有 p 兄弟元素,所以 p ~ h2 让它变成蓝色——即使它紧跟在 h1 后面而不是 p。Momo、Nemo、Willow、Leo 是粗体(ul > li);而嵌套在 <ol> 里的 Prozac、Antibiotic、Churu 不是。HTML 里提出的问题是:如果把嵌套列表改成 ul 会怎样?为什么?——它们会变成粗体,因为现在它们也是 ul 的直接子元素了(demo 的最后一步)。

{"title": "Slides 42–45 · advancedSelectors.html + advancedSelectors.css (lorem ipsum shortened)", "html": "    <h1>This is a header!</h1>\n    <p>This is a paragraph. Lorem ipsum dolor sit amet…</p>\n    <h1>This is a header following a paragraph!</h1>\n    <p>Yet another paragraph. Lorem ipsum dolor sit amet…</p>\n    <h1>This is a header following a paragraph!</h1>\n    <h2>This is a header following a header</h2>\n    <p>Yet another paragraph. Lorem ipsum dolor sit amet…</p>\n    <p>Prof Reid's cat's medications</p>\n    <ul>\n        <li>Momo</li>\n        <li>Nemo</li>\n        <li>Willow</li>\n        <li>Leo</li>\n            <!-- What happens if I change the nested list to ul...? why? -->\n            <ol>\n                <li>Prozac</li>\n                <li>Antibiotic</li>\n                <li>Churu (very important)</li>\n            </ol>\n    </ul>", "css": "", "steps": [{"note": "Remove the default top margin on h1 and h2.", "css": "h1 {\n    margin-top: 0px;\n}\n\nh2 {\n    margin-top: 0px;\n}"}, {"note": "p + h1 / p + h2: a heading IMMEDIATELY after a paragraph gets 10px back; the h1 right after another h1 doesn't.", "css": "p + h1 {\n\tmargin-top: 10px;\n}\n\np + h2 {\n\tmargin-top: 10px;\n}"}, {"note": "p ~ h2: any h2 that comes after a p sibling (not necessarily right after) turns blue.", "css": "p ~ h2 {\n    color: blue;\n}"}, {"note": "ul > li: only the direct <li> children of the <ul> are bold; the <li>s inside the nested <ol> are not.", "css": "ul > li {\n    font-weight: bold;\n}"}, {"note": "The comment's question: change the nested <ol> to <ul> — now those <li>s are direct children of a ul too, so they turn bold.", "html": "    <h1>This is a header!</h1>\n    <p>This is a paragraph. Lorem ipsum dolor sit amet…</p>\n    <h1>This is a header following a paragraph!</h1>\n    <p>Yet another paragraph. Lorem ipsum dolor sit amet…</p>\n    <h1>This is a header following a paragraph!</h1>\n    <h2>This is a header following a header</h2>\n    <p>Yet another paragraph. Lorem ipsum dolor sit amet…</p>\n    <p>Prof Reid's cat's medications</p>\n    <ul>\n        <li>Momo</li>\n        <li>Nemo</li>\n        <li>Willow</li>\n        <li>Leo</li>\n            <!-- What happens if I change the nested list to ul...? why? -->\n            <ul>\n                <li>Prozac</li>\n                <li>Antibiotic</li>\n                <li>Churu (very important)</li>\n            </ul>\n    </ul>"}], "height": 420, "interactive": true}

🧪 Demo 文件:advancedSelectors.html · advancedSelectors.css

⚠️ 易错点:advancedSelectors.html 里嵌套的 <ol> 直接位于 <ul> 内部,而不是在 Leo 的 <li> 里面。浏览器能渲染它,但合法的 HTML 要求把嵌套列表放在 <li> 内部(幻灯片 43 的树就是这样做的)。

9.1 伪类(Additional slides,课上未讲)

伪类(pseudo-class)选择处于特殊状态的元素:selector:pseudo-class { … }。经典的例子是链接:

a:link { /* 未访问的链接 */
    color: #FF0000;
}
a:visited { /* 已访问的链接 */
    color: #00FF00;
}
a:hover { /* 鼠标悬停在链接上 */
    color: #FF00FF;
}
a:active { /* 被选中的链接 */
    color: #0000FF;
}

这里顺序很重要,因为“后面的规则获胜”::hover 必须写在 :link 和 :visited 之后,:active 必须写在 :hover 之后。

🔗 延伸阅读:Smashing Magazine:驯服高级 CSS 选择器 · CSS-Tricks:子选择器与兄弟选择器 · W3Schools:CSS 伪类

10. 字体与备选方案(fallback)

字体栈(Font stacks)。font-family 接受一个列表;浏览器按顺序尝试这些字体,使用第一个可用的。最后以一个通用字体族(serif、sans-serif)结尾,这样总能得到一个合理的结果:

p {
    font-family: "Times New Roman", Times, serif;
}

Web 字体。Google Fonts 是个很好的资源:在 HTML 中链接它的样式表,然后在 CSS 中使用该字体名并附上备选字体:

<!--HTML-->
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
/* CSS */
font-family: 'Roboto', sans-serif;

幻灯片上把它写成了 rel=“stylesheet">,开头是弯引号;原样粘贴的话,属性值会变成 “stylesheet,字体样式表就永远加载不出来。请使用直引号。

dafont.com(“互联网上最酷的网站,客观地说……?”)有无数开源字体——有些花哨到远超过你在无障碍、易读的网页设计中的需要。

HTML 中的备选方案道理相同:浏览器会选择第一个它能使用的来源。

<video autoplay>
    <!--webm not supported in IE or Safari-->
    <source src="lecture3.webm" type="video/webm" />
    <!--mp4 supported in modern browsers, but lower quality-->
    <source src="lecture3.mp4" type="video/mp4" />
    <!--backup important for some old browsers-->
    <img src="lecture3.jpg" title="Your browser does not support the <video> tag">
</video>

为什么需要备选方案(幻灯片 49):某种格式不被支持(webm、ogg、flac);某个字体可能不支持某些字符;某个字体可能需要时间加载(“flash of unstyled text”,无样式文字闪烁)——所以要选一个相似的默认字体,这样 Web 字体到达时页面不会跳动。

🔗 延伸阅读:Google Fonts · dafont.com · CSS-Tricks:用于稳健网页排版的备选字体栈

11. 工具:参考资料、调试、兼容性、验证

“CSS 的内容太多了。光凭记忆我写不出多少东西。”——Reid 教授。没有人能背下每个属性;需要时去查:

  • 参考资料:W3Schools CSS reference、cssreference.io、MDN CSS reference、Codecademy 的 Learn CSS 课程。
  • 在浏览器中调试:DevTools(Inspect element)会显示元素树、作用于所选元素的每条规则(被覆盖的会画上删除线——层叠的可视化)以及盒模型示意图;你还可以实时修改数值(幻灯片 7–8 和 52)。
  • 浏览器兼容性:标准已经稳定,但仍然值得在 caniuse.com 上查一下某个特性。
  • 验证:W3C Markup Validation Service 会检查你的 HTML(例如重复的 ID)。

🔗 延伸阅读:W3Schools CSS 参考 · cssreference.io · MDN CSS 参考 · Codecademy:Learn CSS · YouTube:在浏览器中调试 CSS · Can I use
🔗 延伸阅读:W3C Markup Validation Service

12. 课堂问题与 Demo(附解答)

12.1 Question 1:哪段 CSS 能渲染出图中的 ZOT! ZOT! ZOT!?(幻灯片 22–27)

投票遵循惯常流程(幻灯片 22):个人作答 → 公布回答 → 全班讨论 → 公布正确答案。

哪段 CSS 能渲染出

<p class="para">
ZOT!
ZOT!
<em id="zot">ZOT!</em>
</p>

这个截图效果:一行文字,UCI 蓝色条上的白色“ZOT! ZOT!”,最后一个 ZOT! 是黄色粗斜体?

  • A
    p {
        font-family: 'Verdana';
        color: #0064a4; /*Blue*/
        background-color: white;
    }
    #em {
        color: #ffd200; /*Yellow*/
        font-weight: 700; /*Bold*/
    }
  • B
    .para {
        font-family: 'Verdana';
        background-color: #0064a4; /*Blue*/
        color: white;
    }
    zot {
        color: #ffd200; /*Yellow*/
        font-style: italic;
        font-weight: 700; /*Bold*/
    }
  • C
    .para {
        font-family: 'Verdana';
        background-color: #0064a4; /*Blue*/
        color: white;
    }
    em {
        color: #ffd200; /*Yellow*/
        font-weight: 700; /*Bold*/
    }
  • D
    #para {
        font-family: 'Verdana';
        background-color: #0064a4; /*Blue*/
        color: white;
    }
    .em {
        color: #ffd200; /*Yellow*/
        font-style: italic;
        font-weight: 700; /*Bold*/
    }
  • E
    .para {
        font-family: 'Verdana';
        background-color: #0064a4; /*Blue*/
        color: #ffd200; /*Yellow*/
    }
    #zot {
        color: white;
        font-style: italic;
        font-weight: 700; /*Bold*/
    }

✍️ 先自己试一试。

答案:C(在幻灯片 27 公布;仓库里的 q1answer.css 就是选项 C)。

.para 通过 class 匹配 <p> → 蓝色背景、白色 Verdana 文字。em 通过标签匹配 <em> → 黄色、粗体。斜体不需要任何规则:<em> 在浏览器默认样式下就是斜体(§2.3)。

选项 实际效果 为什么错
A 白底蓝字;最后的 ZOT! 是蓝色,不是黄色也不是粗体 颜色弄反了,而且 #em 查找的是 id="em",它并不存在
B 色条正确,但最后的 ZOT! 是白色且不是粗体 zot 是标签选择器(没有 <zot> 元素);id 需要写成 #zot
C 蓝底白字,最后的 ZOT! 是黄色、粗体、斜体 ✓
D 什么样式都没有——白底黑字 #para 需要 id="para"(而这里是 class),.em 需要 class="em"
E 黄色的“ZOT! ZOT!”和白色粗斜体的最后一个 ZOT! 选择器正确,颜色弄反了

每个选项的计算样式(背景上的文字颜色、斜体、粗细):

选项 <p> <em>
A rgb(0, 100, 164) 文字,白底 rgb(0, 100, 164),斜体,400
B rgb(0, 100, 164) 底,白字 白色,斜体,400
C rgb(0, 100, 164) 底,白字 rgb(255, 210, 0),斜体,700
D 黑字,无背景 黑色,斜体,400
E rgb(0, 100, 164) 底,rgb(255, 210, 0) 文字 白色,斜体,700
{"title": "Question 1 check · q1answer.html + q1answer.css (option C)", "html": "    <p class=\"para\">ZOT! ZOT! <em id=\"zot\">ZOT!</em></p>", "css": ".para {\n    font-family: Verdana;\n    background-color: #0064a4;\n    color: white;\n}\n\nem {\n    color: #ffd200;\n    font-weight: 700;\n}", "js": "const s = el => { const c = getComputedStyle(el); return c.color + ' on ' + c.backgroundColor + ', ' + c.fontStyle + ', weight ' + c.fontWeight; };\nconsole.log('p:  ' + s(document.querySelector('p')));\nconsole.log('em: ' + s(document.querySelector('em')));", "height": 80}

🧪 Demo 文件:q1answer.html · q1answer.css

返回 §3 元素、类与 ID 选择器。

12.2 Demo 1:给课程表上色(幻灯片 19 · setup1 → solution1)

任务(setup1.css 里的 TODO 清单),针对 setup1.html 中的表格:

  • 把表头行的字体大小增大到 150%
  • 把 discussion 的背景改为 limegreen
  • 把 lecture 的背景改为 royalblue
  • 把 a1 的背景改为 saddlebrown
  • 把 lecture 和 a1 的文字颜色改为白色

关键的标记:表头单元格是 <th>,方块是 <div class="discussion">、<div class="lecture"> 和 <div id="a1">。

✍️ 先自己试一试。

答案(solution1.css):

th {
	font-size: 150%;
}

.discussion {
	background-color: limegreen;
}

.lecture {
	background-color: royalblue;
	color: white;
}

#a1 {
	background-color: saddlebrown;
	color: white;
}

根据 HTML 提供的信息选择选择器:每个表头单元格用标签(th),重复出现的方块用 class(.lecture),唯一的作业用 id(#a1)。结果:更大的星期名称、绿色的 Discussion 方块、带白字的蓝色 Lecture 方块、带白字的棕色“Assignment due”方块。逐步 demo 见 §4。

12.3 Demo 2:边框、padding 与 margin(幻灯片 38 · setup2 → solution2)

setup2.css 以 Demo 1 的解答为起点。任务:

  • 给所有单元格加 1 像素的实线边框
  • 给表头单元格加 5 像素的 padding
  • 给所有 discussion、lecture 和 assignment 加 5 像素的 margin

✍️ 先自己试一试。

答案——solution2.css 新增的行:

td, th {
	border-width: 1px;
	border-style: solid;
}

th {
	/* ...font-size: 150%; from Demo 1... */
	padding: 5px;
}

/* inside .discussion, .lecture and #a1 */
	margin: 5px;

“所有单元格”指 td 和 th 两者,所以用一条带选择器列表的规则。padding 在表头的边框内部;margin 在每个彩色方块的外部,把它从单元格边缘推开 5px——方块不再贴着边框。逐步 demo 见 §6.2。

13. 速查与必记的 5 个要点

选择器

选择器 匹配
p 每个 <p>(元素 / 标签)
.big class 中包含 big 的每个元素
#a1 唯一一个 id="a1" 的元素
div.widget.foo#baz 链式:满足所有条件的那个元素
td, th 选择器列表:任一
ul li 后代:任意深度
ul > li 仅直接子元素
p + h2 紧跟在兄弟元素 p 之后的 h2
p ~ h2 兄弟元素 p 之后任意位置的 h2
a:hover 伪类:处于某种状态的元素

本讲涉及的属性

分组 属性
文本 font-family、font-size、font-weight、font-style、color
盒子 background-color、opacity、width、height、padding(-top…)、margin(-right…)、border(-left…)、border-width、border-style、border-color
布局 position: static | relative | absolute | fixed

单位:px 绝对单位 · em 相对于字体大小 · % 相对于父元素 · in/cm 用于打印。屏幕上优先使用 em/%。

优先级,从高到低:行内 style > #id > .class / :pseudo-class / 属性 > 元素。相同时:后面的规则获胜。

必记的 5 个要点

  1. . 是 class,# 是 id,没有前缀是标签——前缀写错会悄无声息地什么都匹配不到(Question 1)。
  2. 在优先级相同的规则中,CSS 里最后写的那条获胜;class 属性中 class 的顺序无关紧要。
  3. id 在每个页面中是唯一的,一个元素最多只能有一个;class 可以复用,一个元素可以有多个。
  4. 盒模型由内到外:content → padding(会应用背景)→ border(需要 style)→ margin(透明,隔开相邻元素)。
  5. ul li 能选到孙元素,ul > li 不能;p + h2 表示紧跟在后面,p ~ h2 表示在后面的任意位置。

快捷键[ 上一篇] 下一篇L 切换语言