id必须唯一,用于锚点、js单元素获取及label关联;class可复用,是样式主力,应按bem规范命名,多class组合更灵活可维护。

id 和 class 不是“随便加的标识”,而是运行时行为开关
它们直接影响 JS 查找、CSS 匹配、无障碍关联,用错一个,document.getElementById 返回 null,querySelector 选中错误元素,屏幕阅读器跳过关键节点。
- 多个元素用了相同
id:JS 只返回第一个,后续逻辑全断;CSS 仍会命中全部,样式覆盖混乱 -
class值含空格却没引号:<div class="btn" primary></div>中primary被当作文本节点丢弃,不生效 - 动态拼接
class没去重:el.className += " active"多次执行导致active active active,classList.contains("active")仍为true,但调试时极易误判 - 服务端模板未转义:
class="{{ user.role }}"若user.role是"admin script",会意外注入非法 class,甚至触发 XSS
data-* 属性不是“万能存储桶”,有明确读写边界
它只在 JS 中通过 dataset 读写有效,HTML 和 CSS 完全不解析;浏览器对 key 名做隐式转换,值只存字符串,且不响应式。
- 连字符自动转驼峰:
data-user-id="123"→ JS 中必须用dataset.userId,dataset.user-id是语法错误 - 数字开头的 key 被忽略:
data-1st-place="gold"在dataset中不可见 - IE11 不支持大写字母转换:
data-MyKey→mykey(不是myKey) - 别存对象或函数:必须手动
JSON.stringify/JSON.parse,且 Vue/React 中改了 DOM 的dataset,组件状态不会同步
hidden 和 contenteditable 是布尔属性,值无效
只要存在就生效,写成 hidden="false" 或 contenteditable="0" 都没用——它们不是开关,是“存在即启用”。
-
hidden:语义上隐藏,等价于display: none,但更准确;hidden="anything"和hidden效果完全一样 -
contenteditable:只接受字符串"true"或"false";contenteditable="off"或contenteditable=""都无效,会被当成"true" - 两者都不参与 CSS 选择器匹配(
[hidden]有效,但[hidden="false"]永远不匹配)
tabindex 和 title 看似简单,实则限制极多
它们不是“写了就有用”,而是依赖具体上下文和平台支持,漏掉细节就白写。
-
tabindex:只有0或正整数才加入 Tab 焦点流;负值(如-1)仅禁用焦点,不改变顺序;tabindex="0"让非交互元素可聚焦,但需手动绑定键盘事件 -
title:鼠标悬停提示,但移动端 Safari 完全不支持;部分屏幕阅读器默认忽略,不能当作无障碍替代方案 - 两者都受浏览器 UA 样式影响,
title提示延迟、位置、持续时间均不可控;tabindex在iframe内嵌页中需额外处理跨域焦点
真正容易被忽略的是:全局属性不是“语法糖”,而是运行时契约。写一个 id,你就承诺它唯一;加一个 data-,就得自己管序列化;用 hidden,就得接受它绕过所有 CSS 动画和过渡。这些约束不报错,但会在 JS 执行、无障碍测试、SEO 抓取时悄悄失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











