前端学习进度小记3
HTML表单的熟悉和理解
HTML 系统理论笔记:表单
为什么要单独给表单开一个笔记
表单是HTML向后台穿透数据的一个窗口,自然也就是绝大多数 Web 应用与网站与用户交互的核心:登录、注册、搜索、提交评论、支付、文件上传、调查问卷等都依赖表单。表单体验的质量直接影响转化率、用户满意度与安全性。所以理解表单非常的重要。
表单的基础结构与语义元素
<form action="/submit" method="post">
<label for="name">姓名</label>
<input id="name" name="name" type="text" />
<button type="submit">提交</button>
</form>
form元素包裹起来的地方就是一个表单,当我们单机提交的时候,就会生成对应的URL回传给我们的后台服务器进行解析。自然,为了说明我们交的东西什么是什么,就要理解<form> 的重要属性:
action:提交到的 URL(可以是相对或绝对地址)。method:get或post(默认get)。enctype:编码类型,文件上传时为multipart/form-data,默认application/x-www-form-urlencoded。novalidate:禁用浏览器内置表单验证。target:提交后打开方式(例如_blank、_self)。
语义上,使用 <label> 关联输入控件(通过 for 与 input 的 id)可以显著提升可访问性与点击体验。
常见表单控件详解
<input>(最通用)
<input> 有许多 type:
text、password、email、tel、url、search:文本输入,email/url会启用基本格式检查并在移动端唤起相应键盘。number、range:数值输入与范围滑块(注意number仍会以字符串提交)。date、datetime-local、time、month、week:日期/时间选择控件(浏览器原生控件,兼容性注意)。color:颜色选择器。checkbox:多选项开关。radio:单选组(通过同name进行分组)。file:上传文件,支持multiple属性以允许多文件选择。hidden:隐藏字段,常用于携带状态或 token。submit、reset、button:触发提交、重置或普通按钮。
<label>
订阅
<input type="checkbox" name="subscribe" value="yes">
</label>
<!-- 单选组 -->
<label><input type="radio" name="gender" value="m"> 男</label>
<label><input type="radio" name="gender" value="f"> 女</label>
<textarea>
多行文本输入:
<label for="message">留言</label>
<textarea id="message" name="message" rows="6"></textarea>
<select> 与 <option>
下拉或可选项列表:
<label for="country">国家</label>
<select id="country" name="country">
<option value="cn">中国</option>
<option value="us">美国</option>
</select>
支持 multiple 属性进行多选。可以结合 <optgroup> 分组。
其他:<fieldset>、<legend>、<datalist>、<label>
<fieldset>&<legend>:用于分组相关控件并提供组标题,增强语义与无障碍体验。<datalist>:为文本输入提供建议列表(非下拉强制选择)。
<input list="browsers" name="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
</datalist>
笔者决定先到这么多,后面跟JS联动的时候,我们再进一步的整理相关的内容。
CSS的部分笔记
前言
之前的CSS笔记整理的有些草率,这里重新仔细的整理一部分笔记,重新理解一下CSS的三种文本存在格式:行内(inline)、内联(internal/embedded)、外部(external)。也顺便把选择器梳理一下。
重新看行内(inline)、内联(internal/embedded)、外部(external)
首先,搞清楚定义:
- 行内样式(inline style):指直接写在 HTML 元素上的
style属性,例如<div style="color:red">。 - 内联样式 / 嵌入式样式(internal / embedded):指放在 HTML 文档
<head>中的<style>...</style>块内的样式,作用于该 HTML 文档。 - 外部样式表(external stylesheet):指放在单独
.css文件并通过<link rel="stylesheet" href="...">引入的样式。
在代码中,我们直接的体现如下所示
行内样式(inline)
<!-- 直接在元素上写 style -->
<button style="background:#007bff; color:white; padding:8px 12px;">
提交
</button>
内联/嵌入式样式(internal)
<head>
<style>
.btn { background: #007bff; color: white; padding: 8px 12px; }
@media (max-width: 600px) { .btn { padding: 10px; } }
</style>
</head>
<body>
<button class="btn">提交</button>
</body>
外部样式表(external)
<!-- index.html -->
<head>
<link rel="stylesheet" href="/styles/main.css">
</head>
<body>
<button class="btn">提交</button>
</body>
/* /styles/main.css */
.btn {
background: #007bff;
color: white;
padding: 8px 12px;
}
优先级的处理问题
CSS的灵活很容易导致一个严肃的问题。大型工程中,如果出现了实际上重复样式渲染的选择,我们如何裁决。这里,CSS的规则如下
!important声明会提升优先级(先比较!important,再比较普通声明)。- 同为普通声明时,内联样式(
style属性) 的特异性非常高,会覆盖绝大多数外部/内嵌选择器规则(除非外部/内嵌使用!important)。 - 特异性数值示例(从高到低的“权重”理解):
- 内联样式(style attribute) → 记作最高(常表述为 1,0,0,0)
- ID 选择器
#id→ 比如 (0,1,0,0) - 类/属性/伪类
.class,[attr],:hover→ (0,0,1,0) - 元素/伪元素
div,p,::after→ (0,0,0,1)
- 当特异性相同,后出现的规则覆盖先出现的(CSS 文件加载顺序和位置决定「后来者获胜」)。
<div id="box" class="green" style="color: red;">文本</div>
- 若
.green { color: green; }在外部样式中定义,最终文本是 红色(因为内联style覆盖)。 - 如果外部写了
.green { color: green !important; },则 绿色 会胜出(author!important与 author 普通规则比较时!important胜出)。 - 如果内联也写了
style="color:red !important",那内联的!important胜。
三种样式的比较和推介使用
很显然,有三种样式存在的方式,我们就可以在不同的场景下进行灵活选择。
行内样式(inline)
行内样式(inline)的好处就是立刻生效、简洁,适合单次小调整或由 JS 动态设置样式(element.style)。对于临时调试、或邮件模板中(很多邮件客户端不支持外部样式)非常有用。但是现代大型工程显然不推介这种方式,因为它破坏结构与样式分离(HTML 混入样式)规则,难维护、重复代码多;特异性很高,容易引发覆盖困难(“特异性战争”)。除了这种维护困难,性能上也会出现大问题:无法被浏览器单独缓存(每个页面都会重复);最致命的也许是与 CSP(Content Security Policy)冲突:许多站点禁止 style-src 'unsafe-inline',所以行内可能被阻止。所以笔者注意到,除非有很正当的理由,否则不要用。
内联/嵌入式样式(<style>)
内联/嵌入式样式便于将某个页面的样式集中在同一文件中,适合单页或独立页面的局部样式。但是他也有麻烦,比如说不同页面之间不能被缓存重用(如果多个页面都内嵌同样样式,会重复传输),而且,好像还是没有分离的很开,仍然会给我们的开发造成大量的麻烦,所以这种方式只有在单页应用或页面级样式、调试、快速原型、或构建时将 critical CSS 放到 <head> 中以优化首屏渲染的时候使用
外部样式表(.css 文件 + <link>)⭐⭐⭐⭐⭐
这才是大家使用的最多的方式,外部样式表可以让我们的渲染样式集中、可重用、便于维护、可以被浏览器缓存,减少重复传输;此外易于与构建工具和版本控制配合
CSP(Content Security Policy)与内联样式
很多站点出于安全会设置 CSP,例如:
Content-Security-Policy: style-src 'self';
这会拒绝任何行内样式或未授权的内联 <style>。
选择器:选择器的概念与分类(为什么选择器重要)
很经典的问题:你怎么让浏览器知道你要渲染谁,对哪一个元素进行渲染?选择器就是告诉浏览器——你要渲染哪一批元素。他就是CSS 的“定位”机制:告诉浏览器把哪些样式应用到文档中哪些元素上。
- 简单选择器(类型/类/ID/通配符)
- 属性选择器(基于元素属性)
- 组合器(描述元素之间的关系)
- 伪类 / 伪元素(状态与虚拟元素)
- 选择器分组(用逗号合并多个选择器)
选择器写得好,样式更可读、可维护、且能避免不必要的渲染开销。
基本(简单)选择器详解
元素选择器(type selector)
这个选择器是直接Target到了指定的元素了,比如说p标签,比如说div标签等等。
p { line-height: 1.6; }
nav { display: flex; }
所以这种标签主要用的是针对全局的设置。
类选择器(class selector)
最常用、推荐的选择方式。以 . 开头,匹配 class 属性包含该类名的元素。
.btn { padding: 8px 12px; }
.card.header { /* 同时包含 .card 和 .header */ }
低特异性、复用性高、和组件化配合良好(BEM 等)。
ID 选择器(id selector)
以 # 开头,匹配 id 属性相同的单个元素。
#main { max-width: 1200px; margin: 0 auto; }
注意:ID 的特异性高,且通常页面中只应出现一次。现代团队常建议用类替代 ID 做样式(ID 多用于 JS 钩子或锚点)。
通配选择器(universal selector)
*,匹配所有元素(注意性能与广度)。
* { box-sizing: border-box; }
注意的是,这种选择器往往在开发中被禁用掉(或者是拿来做清除某一些效果用的)
属性选择器(attribute selectors)
属性选择器通过元素属性筛选元素,形式灵活,常用于无类库或第三方组件的定制。
[attr]:存在某属性
input[required] { border-left: 3px solid #f00; }[attr="value"]:完全匹配
a[target="_blank"] { /* 新窗口链接 */ }[attr~="value"]:空格分隔的词列表中匹配一个词(常用于 class 备份场景)
*[class~="btn"] { /* 等同于 .btn */ }[attr|="value"]:以 value 或 value- 开头(常用于语言)
html[lang|="en"] { }[attr^="value"]:以 value 开头(prefix)
a[href^="mailto:"] { /* 邮件链接 */ }[attr$="value"]:以 value 结尾(suffix)
img[src$=".svg"] { }[attr*="value"]:包含 value(substring)
a[href*="example.com"] { }
属性选择器对动态属性和第三方生成的 DOM 很有用,但表达式复杂时要注意性能与可读性。
组合器(combinators)与关系选择器
描述元素之间的父子/兄弟/后代关系。
-
后代选择器(space):
A B:匹配位于A后代(任意层级)中的Barticle p { margin-bottom: 1rem; } -
子选择器(
>):A > B:匹配A的直接子元素B(这里谈到的子是直接的儿子,不包含孙子曾孙子等)ul > li { list-style: none; } -
相邻兄弟选择器(
+):A + B:匹配直接跟在A之后的兄弟Bh2 + p { margin-top: 0.25rem; } -
通用兄弟选择器(
~):A ~ B:匹配在同一父元素下、位于A之后的所有兄弟Binput:checked ~ .details { display: block; }
选择器越具体、越嵌套越深,维护成本越高。优先使用类并限制层级深度(例如不超过 3 层复杂链)。
伪类(pseudo-classes)与伪元素(pseudo-elements)
常见伪类(表示元素状态)
:hover、:active、:focus、:visited、:checked、:disabled、:nth-child()、:nth-of-type()、:not()等。
button:hover { transform: translateY(-2px); }
li:nth-child(odd) { background: #f8f8f8; }
a:visited { color: purple; }
:not() 是选择器否定伪类;注意它本身不增加特异性,但它参数的特异性会被计入
常见伪元素(创建虚拟子元素)
::before、::after、::first-line、::first-letter等(现代语法使用双冒号::,单冒号兼容旧浏览器)。
.btn::before { content: "★ "; }
p::first-letter { font-size: 2em; float: left; }
伪类用于“状态”、伪元素用于“生成内容或样式化文档的一部分”。两类常搭配使用(例如 a:hover::after { ... })。
选择器优先级(specificity)与覆盖规则
基本原则
按优先级顺序比较:
- Inline 样式(
style="")最高(记作1,0,0,0) - ID 选择器(
#id)计入到第二位(比如0,1,0,0) - 类/属性/伪类(
.class,[attr],:hover)计入第三位(0,0,1,0) - 元素/伪元素(
div,::after)计入第四位(0,0,0,1) !important可以覆盖常规顺序(优先比较是否!important,再比较特异性)
表示法:把特异性写成
(a,b,c,d)四元组,逐位比较,先看 a,再看 b,以此类推。
Example: 数字逐位计算(按要求逐位算清楚)
选择器:
#nav .item > a:hover::after我们把它拆解计数:
#nav→ ID:计为b = 1.item→ 类:计为c = 1a→ 元素:计为d = 1:hover→ 伪类:也是类级别,计为c += 1::after→ 伪元素:元素级,计为d += 1逐位汇总(按顺序 a,b,c,d):
a(inline) = 0b(ID) = 1c(类/伪类/属性) = 1(.item) + 1 (:hover) = 2d(元素/伪元素) = 1 (a) + 1 (::after) = 2最终特异性 =
(0, 1, 2, 2)。任何具有更高b(ID)或在b相同下更高c的规则会覆盖本规则;若所有位相同,则文档中后出现的规则胜出(later wins)。
7 — 选择器分组、层叠规则与顺序
- 分组选择器(用逗号):复用样式,等价于多条独立规则,但写起来更短。
h1, h2, h3 { font-family: "Noto Sans", sans-serif; }
- 层叠(Cascading)规则:当多个规则匹配同一元素,按照先比较
!important,再比较来源(user/author/UA),再比较特异性,最后比较出现顺序(later wins)。
提示:常见错误是把样式写在多个文件,然后忘了加载顺序,导致覆盖混乱。保持样式文件结构清晰,约定好全局与组件样式的加载顺序。