id必须唯一,重复会导致getElementById只返回第一个元素,破坏锚点跳转、label绑定和ARIA关联;class用于语义分组而非样式钩子,应避免表现耦合;仅锚点、label for、ARIA引用三类场景必须用id。

id 必须唯一,重复会导致 getElementById 只返回第一个
浏览器不校验 id 是否重复,但 document.getElementById("nav") 永远只返回 DOM 中第一个匹配的元素,后续同名 id 完全不可见。常见于 Vue/React 循环渲染时硬写 id="modal",结果多个弹窗共享一个 id —— 表单焦点跳转、ARIA 关联(如 aria-labelledby="title")全部失效。
验证是否重复:在控制台运行 document.querySelectorAll('[id="nav"]'),若返回长度大于 1,就已违规。
修复方式不是“尽量别重复”,而是主动构造唯一值:
– 服务端模板中拼接数据 ID:id="user-card-<font color="#0066cc">item.id</font>"
– 客户端动态生成:id="tooltip-${Math.random().toString(36).slice(2, 9)}"
class 本质是分组标签,不是样式钩子
class 的作用不是“写个名字让 CSS 能选中”,而是给元素打语义化标签:比如 class="btn js-submit-form" 同时表达“这是按钮”和“这个按钮要触发表单提交”。它天然支持叠加、复用、动态增删。
典型误用:
– 把 class="active" 当作唯一状态标识,结果 document.querySelector(".active") 总拿第一个,而非当前激活项
– 在 HTML 里写 class="red-bg big-text center",把表现强耦合进结构,换主题就得改 HTML
推荐写法:
– 用 BEM 或语义命名:class="nav-item nav-item--current"
– 功能类分离:class="card card--featured js-expandable"
– 状态交由 JS 控制:element.classList.add("is-loading"),而非手动写死
什么时候非用 id 不可
只有三类场景必须用 id:
– 锚点跳转:<a href="#section3">跳到第三节</a> + <div id="section3">
<br>
– 表单 label 绑定:<code><label for="email">邮箱</label> 要求对应 <input id="email">,class 在这里完全无效
– ARIA 引用目标:aria-describedby="error-msg" 必须指向一个有 id="error-msg" 的元素
其他情况——比如想给某个弹窗容器加样式或 JS 控制——优先用 class="modal modal--settings" + data-modal-id="settings",既保持语义清晰,又规避唯一性风险。
立即学习“前端免费学习笔记(深入)”;
现代开发中 class 是主力,id 是特例
CSS 权重上 #header 确实高于 .header,但这不是优势而是隐患:一旦第三方库或组件内联了 #sidebar 样式,你用 .sidebar.is-collapsed 都很难覆盖它。
真正影响开发效率的不是选择器性能(现代浏览器已无感知),而是可维护性:
– .nav-link 重构 HTML 结构(li → div)仍生效
– #main-nav > ul > li > a 一改结构就全挂
– 多个 class 共存比一堆 id 更易组合:class="btn btn--primary btn--lg" 比 id="big-primary-button" 更灵活
容易被忽略的点:SSR 或静态生成时,id 冲突往往在上线后才暴露——比如用户反馈“表单验证总聚焦第一个输入框”,查半天才发现模板循环没做 id 动态化。这种问题不会报错,但会悄悄破坏无障碍和交互逻辑。



















