<p>真正影响运行时行为的全局属性仅有 id、class、data-、tabindex、hidden、contenteditable、title、lang、dir 等少数几个;其中 id 和 class 是 JS 选择器与 CSS 引擎唯二深度依赖的定位依据,data- 仅 JS 可读写且遵循驼峰转换规则,tabindex 必须为 0 或正整数才参与焦点流,hidden 和 contenteditable 为布尔属性——存在即生效,值无效。</p>

真正影响运行时行为的全局属性只有几个,其余多数只是语法合法但无实际效果——加了≠起作用,用错一个就可能让 getElementById 拿不到元素、dataset 读不出值、tabindex 失效或无障碍支持崩掉。
哪些全局属性真正在 JS 和 CSS 中起作用
别被“所有标签都能加”误导。浏览器对全局属性的解析有明确边界:
-
id和class是唯二被 JS 选择器和 CSS 引擎深度依赖的全局属性:document.getElementById、querySelector、CSS 类选择器都靠它们定位 -
data-*只在 JS 中通过element.dataset.xxx读写有效;HTML 和 CSS 完全不解析它 -
tabindex必须是0或正整数才参与 Tab 焦点流;负值(如-1)仅禁用键盘聚焦,不改变顺序 -
hidden和contenteditable是布尔属性:存在即生效,值无效 ——hidden="false"依然隐藏,contenteditable="off"仍可编辑 -
title、lang、dir不改样式也不触发行内逻辑,但缺失会导致屏幕阅读器误读、SEO 降权、移动端提示不出现
id 和 class 的典型翻车场景
这两个最常用,也最容易在动态渲染中出问题:
- 多个元素用了相同
id:getElementById只返回第一个,后续 JS 逻辑全错;CSS 选择器却仍会命中全部,造成样式覆盖混乱 -
class值含空格却没引号:<div class="btn" primary>实际只生效btn,primary被当作文本节点丢弃 - 动态拼接
class时没去重:el.className += " active"执行多次导致active active active,classList.contains("active")会返回true,但字符串判断失效 - 服务端模板未转义:
class="{{ user.role }}"若user.role是"admin script",会意外注入非法 class 并可能触发 XSS
data-* 的读写规则与兼容性陷阱
看似自由,实则受浏览器隐式转换约束:
立即学习“前端免费学习笔记(深入)”;
- 连字符自动转驼峰:
data-user-id="123"→ JS 中必须用dataset.userId,不是dataset.user-id(语法错误) - 数字开头的 key 被忽略:
data-1st-place="gold"在dataset中不可见 - IE11 支持
dataset,但不支持带大写字母的转换:data-MyKey→mykey,而非myKey - 不要存大对象或函数:它只序列化为字符串;若塞入 JSON 字符串,取出来得自己
JSON.parse - Vue/React 中,直接改 DOM 的
data-*属性不会触发响应式更新,组件状态不同步
布尔属性的“存在即生效”本质
像 hidden、autofocus、draggable 这类布尔属性,浏览器只检测是否出现在标签中,不解析值:
-
hidden写成hidden="hidden"、hidden=""、甚至hidden="false"效果完全一样 —— 都隐藏 -
contenteditable="0"或contenteditable="off"都无效;合法值只有"true"或"false"(字符串) -
tabindex="-1"不代表“禁用”,而是“可编程聚焦但不进 Tab 流”;给<div>加tabindex="0"才让它能被 Tab 键选中 -
draggable默认值是auto,不是false;图片、链接默认可拖,<div>默认不可拖,除非显式设为true
真正难的不是记住有哪些属性,而是理解每个属性的“行为边界”——它在哪生效、在哪失效、值有没有语义、浏览器怎么解析。漏掉一个细节,比如 data- 的驼峰转换规则或 tabindex 的负值含义,调试时就会卡半天。



















