contenteditable单独使用无效,必须配合tabindex="0"才能键盘聚焦;data-*属性名含连字符时JS需转驼峰读取;hidden在<script>和<meta>上不生效——均属规范行为,非bug。

contenteditable 单独写没用,必须配 tabindex="0" 才能键盘聚焦;data-* 属性名含连字符,JS 里得转驼峰读;hidden 在 <script> 和 <meta> 上完全不生效——这些不是 bug,是规范行为。
contenteditable 必须和 tabindex 组合使用
只写 contenteditable="true",元素看起来可编辑,但点不进去、Tab 不进去、屏幕阅读器也跳过——这不是浏览器兼容问题,是漏了 tabindex="0"。WCAG 2.1.1 明确要求键盘可访问性,缺这个组合就直接违规。
常见错误现象:
- 光标悬停无反应,点击无焦点
- 按 Tab 键跳过该元素
- 中文输入法无法激活,或符号键失灵(常因子元素有
user-select: none或pointer-events: none)
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 启用时同步设置:
el.setAttribute('contenteditable', 'true')+el.tabIndex = 0 - 禁用时还原:
el.setAttribute('contenteditable', 'false')+el.tabIndex = -1 - 加
spellcheck="false"避免下划线干扰 UI
data-* 属性命名与读取的大小写陷阱
HTML 中写 data-user-id="123",JS 里必须用 dataset.userId 访问,不是 dataset.userid 或 dataset.Userid。连字符自动转驼峰,且首字母小写,大小写错一个就拿不到值。
容易踩的坑:
- 属性名以数字开头,如
data-1st-place→ 不能用dataset.1stPlace(语法非法),得用dataset["1st-place"] - 属性名含冒号、空格或 Unicode 字符,如
data-user:name→ 浏览器静默忽略,不报错也不存 - 值永远是字符串:
data-count="0"→el.dataset.count === "0",需手动Number()或parseInt()
注意:React/Vue 等框架不响应 dataset 变更,el.setAttribute('data-id', '500') 不会触发组件重渲染。
hidden 属性不是万能隐藏开关
hidden 是布尔属性,写成 hidden 或 hidden="" 效果一样,等价于 display: none。但它在某些元素上根本不起作用——不报错、不警告,只是安静失效。
典型失效场景:
-
<script hidden>console.log(1)</script>→ 脚本仍会执行 -
<meta hidden name="viewport">→ 浏览器忽略hidden,仍解析 meta
替代方案:
- 需要逻辑控制显示/隐藏,优先用 CSS 类(如
.is-hidden { display: none; }) - 动态操作时,用
el.style.display = 'none'或el.hidden = true(仅对支持元素有效) - 不要把
hidden和contenteditable同时加在同一个容器上,易导致可聚焦但不可编辑的矛盾状态
tabindex 值不是“越大越靠前”
tabindex 控制的是语义顺序,不是视觉排序。正数(如 tabindex="5")会强行插入 tab 流,打乱 DOM 自然顺序,屏幕阅读器可能跳读或重复播报。
正确用法:
-
tabindex="0":按 DOM 顺序加入 tab 流,95% 场景该用这个 -
tabindex="-1":仅支持 JS 聚焦(el.focus()),不进自然 tab 流,适合弹窗打开后手动聚焦 -
tabindex缺失:元素默认不可键盘聚焦(<div>、<p>等非表单元素)
特别注意:contenteditable 元素若没设 tabindex="0",等于放弃键盘可访问性,且多数测试工具不会主动提示,只能靠人工检查或 axe 等无障碍扫描器发现。



















