真正影响运行时行为的全局属性仅有少数几个:id和class决定js选择与css匹配;data-*仅在js中通过dataset读写有效;tabindex需为0或正整数才参与焦点流;hidden和contenteditable为布尔属性,存在即生效,值无效。

HTML 全局属性不是“所有标签都能随便加”的装饰项,而是有明确行为边界、影响 JS 查找、CSS 匹配、无障碍支持甚至渲染流程的控制开关——用错一个,document.getElementById 就拿不到元素,dataset 读不出值,tabindex 不进焦点流,hidden 看似写了却没隐藏。
哪些全局属性真正在运行时起作用
别被“33 个全局属性”吓住。真正触发浏览器行为的只有少数几个:
-
id和class:JS 选择器和 CSS 引擎直接依赖它们;id是 O(1) 哈希查找,class是遍历匹配,深层 DOM 下性能差异明显 -
data-*:只在 JS 中通过element.dataset读写有效;HTML 和 CSS 完全不解析它 -
tabindex:必须是0或正整数才参与 Tab 焦点流;tabindex="-1"只允许 JS 主动聚焦,不进自然焦点顺序 -
hidden和contenteditable:布尔属性,存在即生效;写成hidden="false"或contenteditable="off"依然触发默认行为 -
lang和dir:不改样式,但缺失会导致屏幕阅读器误读、RTL 文本排版错乱、SEO 语言识别失败
class 和 id 的实际踩坑点
这两个最常用,也最容易在动态场景中翻车:
- 多个元素用了相同
id:document.getElementById("x")永远只返回第一个,后续 JS 逻辑全部失效;而 CSS#x仍会命中全部,造成样式覆盖混乱 -
class值含多余空格:class="btn primary"(两个空格)会被解析为三个类名:["btn", "", "primary"],导致el.classList.contains("primary")返回false - 拼接 class 用错了方式:
el.className += " active"多次执行会变成"btn active active active",虽不影响 CSS,但classList.remove("active")只删一次,残留冗余 - 服务端模板未转义:
class="{{ user.role }}"若user.role === "admin script",会意外注入非法 class 并可能触发 XSS
data-* 属性的命名与读取规则
data-* 看似自由,但浏览器有隐式转换逻辑,写错就取不到值:
- HTML 中写
data-user-id="123",JS 中必须用el.dataset.userId(连字符变驼峰,首字母小写),不是el.dataset.userid或el.dataset.UserID - 数字开头的 key 不合法:
data-1st-place="gold"在dataset中不可见,得改用el.dataset["1st-place"] - 属性名含冒号或空格:
data-user:name或data-user name会被浏览器静默忽略,不报错也不存 - 值永远是字符串:
data-count="0"取出来是"0",需要手动转类型:Number(el.dataset.count)
hidden 与 display: none 的关键区别
它们看起来都“让元素看不见”,但底层机制完全不同:
-
hidden是布尔属性,写成hidden、hidden=""、hidden="anything"效果一样,等价于display: none;但hidden="until-found"是特例,等价于content-visibility: hidden,元素仍参与布局 -
hidden会影响可访问性树:屏幕阅读器默认跳过hidden元素;而display: none同样跳过,但visibility: hidden或opacity: 0不会——后者仍被读屏软件感知 -
hidden对<script></script>、<meta>、内元素无效,浏览器直接忽略,不报错也不渲染
真正难的不是记住有多少个全局属性,而是理解每个属性在 DOM 解析、JS 执行、CSS 匹配、无障碍树构建这四个环节中分别扮演什么角色——漏掉任一环节,行为就和预期对不上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











