class和id不是随意分类名,它们决定JS选中、CSS生效和无障碍识别;错误使用会导致逻辑失效,如className+=重复添加、data-*命名不合法、contenteditable缺tabindex等。

class 和 id 不是“随便加的分类名”,它们直接决定 JS 能否选中、CSS 能否生效、无障碍能否识别——加错位置、写错格式,后面所有逻辑都可能失效。
为什么 class 多次拼接会出问题
动态添加 class 时用 el.className += " active" 看似简单,但会导致重复值堆积:active active active。虽然 CSS 仍能匹配,但 JS 判断 el.classList.contains("active") 会始终为 true,而 el.classList.toggle("active") 行为异常。
- 正确做法:统一用
el.classList.add("active")或el.classList.toggle("active") - 服务端模板里避免直接插值:
class="{{ user.role }}"若user.role是"admin script",会意外引入非法 class,甚至触发 XSS -
class值含空格必须加引号:<div class="btn primary">✅,<div class="btn" primary>❌(primary被当作文本节点丢弃)
data-* 属性命名和读取不匹配的典型表现
写 data-userId="123",JS 却读不到 dataset.userId;写 data-1st-place="gold",结果 dataset["1stPlace"] 是 undefined——这不是 JS 错了,是浏览器根本不解析。
- 合法命名只允许小写字母、数字、短横线(
-),不能有大写字母或下划线:data-user-id✅,data-userId❌ - 连字符自动转驼峰:
data-user-id="123"→dataset.userId,不是dataset.user-id(语法错误) - 数字开头的 key(如
data-1st-place)在 dataset 中被完全忽略,IE11 还不支持大写字母转换(data-MyKey→mykey,而非myKey) - 存对象必须手动序列化:
dataset.config = JSON.stringify(obj),取时再JSON.parse(dataset.config)
contenteditable 加了却点不进去?缺的往往是 tabindex
contenteditable="true" 单独存在时,元素无法通过 Tab 进入、鼠标点击无响应、中文输入法打不开——这不是浏览器 bug,而是可访问性规范强制要求:必须显式声明 tabindex="0" 才算“键盘可达”。
立即学习“前端免费学习笔记(深入)”;
- 只写
contenteditable,等价于contenteditable="true",但焦点流缺失,WCAG 2.1 2.1.1 直接不通过 -
tabindex="-1"可用 JS 聚焦(el.focus()),但不在自然 tab 流中;tabindex="5"强制排序,反而破坏屏幕阅读器逻辑 - 子元素若设了
user-select: none或pointer-events: none,会拦截光标定位,出现“能聚焦但无法输入”的假象 - 最小可用组合:
<div contenteditable="true" tabindex="0" spellcheck="false">,后者还能去掉拼写下划线干扰
hidden 和 contenteditable 这类布尔属性的值无效陷阱
hidden="false" 依然隐藏,contenteditable="0" 或 contenteditable="off" 全部无效——HTML 规范只认三个值:true、false、空字符串(等价于 true)。
- 布尔属性的存在即生效:
<div hidden>和<div hidden="anything">效果完全一样 -
spellcheck="false"有效,但spellcheck="0"或spellcheck="no"会被忽略 -
inert属性虽属全局,但 Chrome 122+、Firefox 125+ 才稳定支持,Safari 当前不支持,别默认它可用
真正难的不是记住哪些属性能加,而是理解每个属性背后的行为边界:一个 tabindex 缺失让编辑区域不可达,一个 data- 命名违规让 JS 读不到数据,一个没引号的 class 让样式突然失效——这些都不是“差不多就行”的地方。



















