大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
HTML 属性
这一课你将能做到
- 正确使用 id、class、style、title、hidden、contenteditable、tabindex、lang、dir、translate 等全局属性
- 用 data-* 自定义属性存储私有数据,并通过 JS 的 dataset API 安全读写
- 理解 ARIA 的角色(role)与状态属性,知道何时该用、何时不该用
为什么要学
属性是 HTML 元素的「配置项」。id 和 class 决定 CSS 和 JS 怎么找到你;data-* 让元素携带业务数据;tabindex 决定键盘导航顺序;hidden 和 contenteditable 控制可见性与可编辑性。用错属性,CSS 选择器失效、JS 取不到数据、键盘用户走不出弹窗、屏幕阅读器读错内容。
人话:属性就像给元素贴标签、挂钥匙、装开关——贴错了,后面所有操作都找不到门。
先建立图像
属性的四大类别:
标识定位:id(唯一)/ class(复用)/ style(内联样式)
交互行为:tabindex / contenteditable / draggable / hidden
数据携带:data-* / title / lang / dir / translate
无障碍增强:role / aria-* 系列
核心原则:属性是元素的元数据,不是内容的替代品。能用语义标签表达的,不用属性硬凑。
跟着做
步骤 1:id 与 class
新建 attrs.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>属性演示</title>
<style>
.card { border: 1px solid #ddd; padding: 16px; margin: 8px; }
.highlight { background-color: #fff3cd; }
#special-card { border-color: #007bff; }
</style>
</head>
<body>
<!-- id 唯一,全页只能有一个 special-card -->
<div id="special-card" class="card highlight">
<h2>特殊卡片</h2>
<p>id 唯一,class 可多个。</p>
</div>
<div class="card">
<h2>普通卡片</h2>
<p>只有 class,没有 id。</p>
</div>
</body>
</html>
id:全页唯一,JSgetElementById用,CSS#id优先级最高。一个元素只能有一个 id。class:可多个,空格分隔,JSgetElementsByClassName/querySelectorAll用,CSS.class可复用。
步骤 2:data-* 自定义属性
<!-- 携带业务数据,不显示在页面 -->
<div class="product"
data-id="1001"
data-price="299.00"
data-category="electronics"
data-stock="15">
<h3>无线耳机</h3>
<p>价格:¥299</p>
<button onclick="addToCart(this)">加入购物车</button>
</div>
<script>
function addToCart(btn) {
const product = btn.closest('.product');
// dataset 自动把 data-* 转成 camelCase
const id = product.dataset.id; // "1001"
const price = parseFloat(product.dataset.price); // 299.00
const category = product.dataset.category; // "electronics"
const stock = parseInt(product.dataset.stock); // 15
console.log(`加入购物车:${id},价格 ${price},分类 ${category},库存 ${stock}`);
}
</script>
命名规则:
- HTML 里:
data-user-id、data-price - JS dataset:
dataset.userId、dataset.price - 自动转换:连字符转 camelCase,
data-user-id→dataset.userId
步骤 3:hidden、contenteditable、tabindex
<!-- hidden:条件渲染,比 CSS display:none 语义化 -->
<div id="loading" hidden>加载中...</div>
<button onclick="document.getElementById('loading').hidden = false">显示加载</button>
<!-- contenteditable:富文本编辑器基础 -->
<div contenteditable="true" style="border: 1px solid #ccc; padding: 8px; min-height: 100px;">
在这里输入内容,可以<strong>加粗</strong>、<em>斜体</em>。
</div>
<!-- tabindex:控制键盘 Tab 顺序 -->
<form>
<input type="text" placeholder="第一个" tabindex="1">
<input type="text" placeholder="第三个" tabindex="3">
<input type="text" placeholder="第二个" tabindex="2">
<!-- tabindex="0":按文档顺序,可聚焦 -->
<!-- tabindex="-1":不可 Tab 聚焦,但可 JS focus() -->
</form>
tabindex 规则:
- 正整数:按数值从小到大顺序 Tab(不推荐,维护困难)
0:按文档顺序,可 Tab 聚焦(推荐用于自定义控件)-1:不可 Tab 聚焦,但 JSfocus()有效(用于弹窗、错误提示)
步骤 4:lang、dir、translate
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
</head>
<body>
<!-- 局部语言覆盖 -->
<p>中文段落。</p>
<p lang="en">This is an English paragraph.</p>
<!-- dir:文字方向 -->
<p dir="ltr">从左到右(默认)</p>
<p dir="rtl">من اليمين إلى اليسار(阿拉伯语)</p>
<!-- translate:告诉翻译工具是否翻译 -->
<p translate="no">BrandName™ 不要翻译</p>
<p>这段可以被 Google 翻译</p>
</body>
</html>
lang:屏幕阅读器据此切换语音引擎,搜索引擎据此判断语言。dir:ltr(默认,从左到右)、rtl(从右到左,阿拉伯语/希伯来语)。translate:yes(默认)或no,保护品牌名、代码不被机翻。
步骤 5:ARIA 简介
ARIA(Accessible Rich Internet Applications)是给辅助技术(屏幕阅读器)提供额外信息的属性集。当原生 HTML 语义不足时,用 ARIA 补充。
<!-- 原生语义足够时,不用 ARIA -->
<button>提交</button> <!-- 屏幕阅读器读:按钮,提交 -->
<!-- 自定义控件无原生语义,必须加 ARIA -->
<div role="button" tabindex="0" aria-pressed="false"
onclick="toggle()" onkeydown="if(event.key==='Enter')toggle()">
自定义开关
</div>
<!-- 状态提示 -->
<div role="alert" aria-live="assertive">
表单提交失败,请检查网络。
</div>
<!-- 进度条 -->
<div role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100">
60%
</div>
<!-- 标签补充:图标按钮无文字 -->
<button aria-label="关闭弹窗">×</button>
<!-- 关联描述 -->
<input type="text" aria-describedby="pwd-tip" aria-required="true">
<p id="pwd-tip">密码至少 8 位,包含字母和数字</p>
ARIA 第一原则:能用原生 HTML 语义(button、nav、main),就不用 ARIA。ARIA 是补丁,不是首选。
讲清楚要点
- id 唯一,class 复用:id 是身份证号,class 是衣服标签。一个元素只能有一个 id,但可有多组 class。
- data- 是业务数据的合法口袋*:不要把数据塞 class 或 id 里(
class="price-299"是反模式),data-* 语义清晰、JS 读写方便、不影响样式。 - tabindex 是键盘导航的生命线:自定义可交互元素(div 按钮)必须加 tabindex="0",否则键盘用户无法触及。
- hidden 与 CSS display:none 等价但语义化:hidden 是布尔属性,表示「与当前状态无关」,比纯样式隐藏更明确。
- ARIA 是补充,不是替代:原生 button 自带 role、键盘事件、焦点管理。用 div + ARIA 模拟按钮,你要自己实现所有这些——能不用就不用。
你可能会懵的地方
-
class 和 id 的 CSS 优先级? id 选择器(#id)优先级高于 class(.class)。但工程实践推荐全部用 class,id 只留给 JS 锚点,避免优先级战争。
-
data- 属性的值有类型吗?* 没有,全是字符串。
dataset.age返回"25"不是25,需要parseInt/parseFloat/JSON.parse手动转换。 -
contenteditable 的安全风险? 用户可粘贴任意 HTML(含 script),需配合 DOMPurify 等库消毒。不要直接把 contenteditable 的 innerHTML 存数据库。
-
translate="no" 真的有用吗? 对 Google 翻译等主流工具有效,但不保证所有翻译插件遵守。品牌名、代码片段、人名建议加。
-
ARIA 加了越多越好? 恰恰相反。错误使用 ARIA 比不用更糟:role="button" 的 div 不如直接用 button;多余的 aria-label 会覆盖屏幕阅读器的自然朗读。无 ARIA > 坏 ARIA。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
class="price-99" data-price="99" 双份数据 | 维护困难,可能不同步 | 只用 data-*,样式通过 class |
tabindex="5" 用正整数 | 顺序硬编码,插入新元素全乱 | tabindex="0" 按文档顺序 |
| div 模拟按钮无 tabindex | 键盘无法聚焦 | tabindex="0" + keydown 处理 |
| 图标按钮无 aria-label | 屏幕阅读器读「按钮」无意义 | aria-label="描述" 或加视觉隐藏文本 |
用 id="list" 给 10 个元素 | id 重复,JS 只取第一个 | 改用 class |
| contenteditable 直接存 innerHTML | XSS 攻击入口 | 消毒后再存储 |
卡点急救
- dataset 取不到值:检查 HTML 属性名是否带
data-前缀;JS 里dataset.userId对应data-user-id,不是data-userId。 - tabindex 顺序混乱:正整数 tabindex 是全局排序,新增元素要改所有后续 tabindex。重构为全用
tabindex="0"。 - hidden 属性不生效:某些 CSS 框架覆盖了
[hidden] { display: none; }。检查样式表,或改用 class 控制。 - aria-label 不朗读:屏幕阅读器模式问题,或元素 role 不支持 aria-label(如 role="presentation")。换用 aria-labelledby 或检查 role 兼容性。
面试常问
-
data- 属性的使用场景?* 存储页面私有数据,供 JS 读取:商品 ID、配置开关、状态标记。替代方案:① 塞 class/id(反模式,污染样式);② 全局 JS 变量(难维护,多实例冲突);③ data-*(语义清晰,元素级封装)。注意:不存敏感数据(HTML 源码可见)。
-
ARIA 是什么?什么时候用? Accessible Rich Internet Applications,为辅助技术提供语义补充。使用场景:① 自定义控件(div 模拟按钮、滑块);② 动态状态更新(加载提示、错误消息);③ 复杂关系(标签页、树形菜单)。原则:优先原生 HTML,ARIA 补丁。
-
tabindex 的取值和作用? -1:程序可聚焦(JS focus),Tab 不可达;0:按文档顺序 Tab 可达;正整数:按数值顺序 Tab(不推荐)。自定义可交互元素必须考虑 tabindex,否则键盘用户无法操作。
-
contenteditable 的注意事项? 富文本编辑的基础。风险:① 粘贴板注入(XSS);② 不同浏览器生成 HTML 结构不一致;③ 样式难以控制。工程实践:配合 DOMPurify 消毒、限制标签白名单、或改用成熟富文本库(Quill、TinyMCE)。
自测三问
- 写出用 data-* 存储商品 ID 和价格,并用 JS 读取的完整代码。
- 为什么自定义 div 按钮必须加 tabindex="0"?
- ARIA 的第一使用原则是什么?
小结一句话
属性是元素的元数据接口:id/class 定位,data-* 存数据,tabindex 管键盘,ARIA 补语义——每个属性都是元素与外界沟通的协议。
延伸
- MDN:HTML 属性参考
- MDN:ARIA
- 下一课:Web 基础——URL 结构、相对/绝对路径、HTTP 方法与 MIME 类型。