hidden 属性是语义层逻辑删除而非 display 开关,contenteditable 必须配 tabindex="0" 才具备可访问性,is 属性仅对白名单原生标签生效且需 customElements.define 的 extends 选项,data-* 属性命名与 dataset 访问需严格遵循驼峰转换规则。

hidden 属性不是 CSS 的 display 开关,它是语义层的“逻辑删除”;contenteditable 不配 tabindex="0" 就等于没写;is 属性只对白名单原生标签生效,且必须配合 customElements.define() 的 extends 选项——这些不是边缘情况,而是全局属性在 DOM 渲染、可访问性、自定义元素链路中真实起作用的底层锚点。
为什么 hidden 加了却还显示?
根本原因不是浏览器不支持,而是你把它当成了布尔值开关或样式覆盖工具:
- 写了
hidden="false"或hidden="0"—— 无效。只要属性存在(哪怕值是空字符串),浏览器就执行隐藏逻辑 - 内联样式强制覆盖:
<div hidden style="display: block"> —— <code>hidden的语义仍生效,但 CSS 优先级更高,视觉上“没隐藏” - 服务端渲染(SSR)场景下没加 CSS 兜底:
[hidden] { display: none !important; },JS 还没执行时页面会闪现内容 - 框架劫持:Vue/React 把
hidden当普通 prop 处理,未透传到真实 DOM 节点 -
tabindex缺失 = 键盘不可达 = WCAG 2.1 2.1.1 可访问性失败 -
contenteditable只认三个值:true、false、空字符串;"plaintext-only"或"on"都被忽略 - 子元素若含
user-select: none或pointer-events: none,会拦截光标定位,导致“能聚焦但无法输入” - 拼写下划线干扰 UI?补上
spellcheck="false"就干净了 - HTML 中必须用宿主标签:
<button is="my-btn"></button>✅,<div is="my-btn"> ❌(直接被忽略) <li> <code>customElements.define("my-btn", MyBtn, { extends: "button" })中的extends选项不可省略;没有它,is就是纯字符串装饰 -
is值大小写、连字符、空格全部敏感:my-btn和MyBtn是不同标识符 - 注册必须在 HTML 解析到含
is的标签前完成,否则首次渲染 fallback 成HTMLUnknownElement - 必须以
data-开头,后接合法 HTML 标识符:不能含大写字母、空格、特殊符号;推荐短横线分隔,如data-user-id="123"✅,data-userId="123"❌ - JS 中必须用
dataset访问:el.dataset.userId→ 实际读的是data-user-id(自动转为驼峰) - 动态设置要用
el.dataset.foo = "bar",而不是setAttribute("data-foo", "bar");后者不会同步到dataset对象
正确操作只有一条:el.hidden = true(添加)或 el.hidden = false(彻底移除)。别用 setAttribute 或 removeAttribute,它们和初始 HTML 状态判断不一致。
contenteditable 必须配 tabindex="0" 才算真正可用
加了 contenteditable="true" 却点不进去、Tab 跳不过去、输不了中文——90% 是因为漏了 tabindex="0":
立即学习“前端免费学习笔记(深入)”;
最小可用写法:<div contenteditable="true" tabindex="0" spellcheck="false">编辑内容</div>
is 属性不是通用增强开关,它依赖严格注册链路
is 只在规范白名单内的原生标签上生效,比如 button、canvas、table、tr,且必须满足两个硬性条件:
Vue 的 :is 是模板编译器语法糖,和 Web Components 规范无任何交集;混用会导致既不是原生按钮,也不是你定义的类实例。
data-* 的命名与读取有硬性规则,不是随便起名就能用
data-* 看似自由,但浏览器对格式和 JS 访问方式有明确限制:
这个转换规则是单向的:HTML 中写 data-user-id,JS 里读 userId;反过来,JS 写 el.dataset.userId = "123",DOM 中会生成 data-user-id 属性——但如果你手写 data-userId,dataset.userId 就读不到。



















