input 不可编辑用 readonly 还是 disabled?提交字段、焦点顺序与校验差异
表单里"不让用户改"这件事,HTML 给了两种完全不同的实现:readonly 与 disabled。它们在网络请求、样式继承、可访问性和校验行为上的结果差别很大,选错就会出现"提交后端收到的字段是空的"这类诡异问题。本文依据 MDN 的 readonly 属性页原文整理。

一、官方支持的控件类型
MDN readonly 页明确:"The readonly attribute is supported by textual form controls",官方示例给出三种写法:
<input id="age" name="age" type="number" value="42" readonly /><textarea id="hobbies" name="hobbies" readonly>Baseball</textarea>
注意它同时列出了 checkbox、radio、submit 等项目——这些是"不受 readonly 影响"的类型,给复选框加 readonly 没有任何效果,要禁用只能靠 disabled 或事件拦截。
二、readonly 的三个真实行为
- 能被聚焦、能选中文字:用户可以把光标移进去、复制内容,这对"展示订单号、邀请码"很关键。
- 值仍会随表单提交:字段有 name,就照常进入请求体,后端能收到。
- 脚本仍可改值:MDN 原文——"The only way to modify dynamically the value of the readonly attribute is through a script."
另外官方一句非常重要:"If the element is read-only, then the element's value cannot be updated by the user, and does not participate in constraint validation." 即 readonly 字段不参与约束校验。这意味着 required、pattern、minlength 对它是无效的,想用 required 兜底空值时要注意这一点。
三、disabled 的差别在哪
两者都阻止用户编辑,但 disabled:
- 不进入 Tab 焦点顺序,键盘用户完全访问不到;
- 表单提交时被排除,后端不会收到该字段,这是最常见的踩坑点;
- 浏览器会套默认置灰样式,需要用 CSS 覆盖才能看清内容;
- 校验同样跳过。
结论很简单:只要这个值需要提交给服务器,就用 readonly;只在"彻底排除该字段"时才用 disabled(例如灰化的可选项)。如果只是想让 select 不可改,select 两个都不支持,得用事件层拦截或覆盖层。
四、纯样式手段的局限
常见做法是给输入框加 pointer-events:none 或 user-select:none,但这只是挡住鼠标,键盘仍可输入,对辅助技术也没有任何声明意义,不能当作"不可编辑"的实现。只读语义必须交给 HTML 属性。
五、无障碍与安全边界
readonly 会把控件的状态暴露给屏幕阅读器,用户知道它是只读的;但视觉与语义要成对出现,否则用户会以为是输入框坏了。若需要更明确的语义,可补 aria-readonly="true"。
安全上必须强调:前端任何只读手段都不具备防护作用。用户可以直接改 DOM、改请求包,或者把 readonly 去掉再提交。价格、权限、账号标识这类字段必须在服务端重新校验与计算,绝不能因为界面上只读就认为不可篡改。
六、常见问题 FAQ
问:为什么后端收不到字段?
答:九成是用了 disabled,或忘了写 name。
问:readonly 会被绕过吗?
答:会,它是交互约束不是安全机制。
问:想让用户能选文字但不能改,怎么办?
答:就用 readonly;若内容不属于表单,直接用普通文本元素更合适。
七、总结
"input 不可编辑"的正确选择是:需要提交值就用 readonly(可聚焦、可选中、仍参与提交、脚本可改,但不参与约束校验,因此 required 对其无效);不希望该字段进入请求才用 disabled(同时会失去键盘焦点并需要覆盖默认置灰样式);checkbox、radio、submit 不支持 readonly,select 两者都不能真正禁用其语义,需换思路;pointer-events 之类只是视觉遮罩。并且始终记住:只读只是交互约束,服务端必须重新校验。
生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »
