
如何根据条件切换文本框只读状态:原生 JavaScript 示例
表单里经常会遇到这样的需求:用户先选择某个选项,系统再决定某个文本框能不能编辑;订单信息可以查看,但不允许修改;字段值由业务规则计算,用户只能确认而不能手动输入。对于这类“允许查看、不允许编辑”的场景,可以使用 HTML 的 readonly 属性,并通过原生 JavaScript 按条件切换。
本文从可运行示例开始,说明如何设置和移除只读状态、哪些表单控件支持只读,以及只读与禁用的区别。还会讨论表单提交、动态更新和安全边界,避免把前端的只读效果误当成数据保护措施。
先弄清 readonly 表示什么
readonly 是一个布尔属性。对支持它的控件来说,属性存在就表示只读;属性值写成 "false" 也不会关闭只读。因此,下面两种写法都表示只读:
<input type="text" readonly>
<input type="text" readonly="readonly">
在 JavaScript 中,推荐通过 DOM 元素的 readOnly 属性设置状态:
const field = document.querySelector("#account");
field.readOnly = true; // 设置为只读
field.readOnly = false; // 恢复可编辑
注意 JavaScript 属性名是 readOnly,其中的 O 是大写;HTML 属性名则是小写的 readonly。不要使用 field.setAttribute("readonly", "false") 来取消只读:属性只要还存在,控件仍然是只读的。要移除属性,可以调用 removeAttribute("readonly"),但直接设置 field.readOnly = false 通常更清楚。
一个可以直接运行的示例
下面的示例提供一个勾选框,用来控制备注框是否可编辑。勾选后,备注框变为只读;取消勾选后,恢复编辑。示例使用浏览器原生 DOM API,不依赖框架或第三方库。将代码保存为 .html 文件后,用浏览器打开即可运行。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>切换文本框只读状态</title>
<style>
body {
max-width: 42rem;
margin: 2rem auto;
padding: 0 1rem;
font: 16px/1.6 sans-serif;
}
label {
display: block;
margin: 0.75rem 0;
}
textarea {
box-sizing: border-box;
width: 100%;
min-height: 7rem;
}
textarea[readonly] {
background: #f2f2f2;
color: #444;
}
#status {
min-height: 1.5em;
}
</style>
</head>
<body>
<h1>备注设置</h1>
<label>
<input id="lock-note" type="checkbox" checked>
锁定备注(只读)
</label>
<label for="note">备注</label>
<textarea id="note" name="note" readonly>订单已确认,请勿修改。</textarea>
<p id="status" aria-live="polite"></p>
<script>
const lockCheckbox = document.querySelector("#lock-note");
const noteField = document.querySelector("#note");
const status = document.querySelector("#status");
function syncReadOnly() {
noteField.readOnly = lockCheckbox.checked;
status.textContent = noteField.readOnly
? "备注当前为只读。"
: "备注当前可以编辑。";
}
lockCheckbox.addEventListener("change", syncReadOnly);
// 首次加载时也根据当前勾选状态同步一次。
syncReadOnly();
</script>
</body>
</html>
这里有几个值得保留的做法:
change事件适合响应复选框状态变化,不需要轮询检查。- 只读状态由
lockCheckbox.checked决定,规则直观,也便于后续扩展。 - 页面加载时主动调用一次
syncReadOnly(),让初始 HTML 状态与控制逻辑保持一致。 - 提示文字通过
textContent更新,而不是拼接 HTML 字符串。 - 通过
:read-only或属性选择器设置视觉样式,不要只改变颜色就假定控件已经不可编辑。
根据其他条件切换
切换条件不一定来自复选框,也可以是单选项、下拉框或已有的业务状态。关键是把“条件计算”和“状态赋值”写清楚。比如只有当用户选择“已有账户”时,账户编号字段才只读:
const accountType = document.querySelector("#account-type");
const accountId = document.querySelector("#account-id");
function updateAccountId() {
accountId.readOnly = accountType.value === "existing";
}
accountType.addEventListener("change", updateAccountId);
updateAccountId();
对应的控件可以这样编写:
<label for="account-type">账户类型</label>
<select id="account-type">
<option value="new">新账户</option>
<option value="existing">已有账户</option>
</select>
<label for="account-id">账户编号</label>
<input id="account-id" name="accountId" type="text">
如果只读规则依赖多个条件,可以把它们组合成一个明确的布尔表达式,例如:
const isSubmitted = document.querySelector("#submitted").checked;
const isOwner = document.querySelector("#is-owner").checked;
accountId.readOnly = isSubmitted || !isOwner;
条件复杂时,建议用命名变量说明业务含义,而不是把多个比较直接堆在属性赋值语句里。这样更容易检查规则是否写反,也方便在条件变化时维护。
哪些控件支持 readonly
readonly 适用于文本输入类控件和多行文本框。常见类型包括 text、search、url、tel、email、password、date、month、week、time、datetime-local、number,以及 <textarea>。具体控件的界面和交互仍由浏览器实现,但只读状态的核心含义是禁止用户直接编辑值。
复选框、单选框、按钮、文件选择框、颜色选择框和滑块等控件不适合用 readonly。例如,给复选框添加 readonly 并不能可靠地阻止用户切换它。对这些控件,如果确实需要禁止操作,应考虑 disabled,同时确认禁用后的提交行为和无障碍体验是否符合需求。
下拉框 <select> 没有标准的 readonly 属性。如果需求是“可查看但不能改选”,可以根据场景选择其他实现,例如用只读文本展示当前值,或使用禁用的下拉框并在提交时单独处理数据。不要给 <select> 随意添加 readonly,然后认为它已被锁定;浏览器不会因此获得通用的只读行为。
readonly 和 disabled 的区别
这两个属性都可能让控件看起来不能编辑,但它们的语义和表单行为不同。
| 行为 | readonly | disabled |
|---|---|---|
| 用户能否编辑控件值 | 支持只读的控件不能编辑 | 控件被禁用,不能正常交互 |
| 表单提交 | 有名称的只读控件通常会随表单提交 | 禁用控件不会作为表单成功控件提交 |
| 键盘焦点与选择 | 通常仍可聚焦和选择文本 | 通常不能像普通控件一样聚焦或操作 |
| 约束验证 | 只读控件不参与约束验证 | 禁用控件不参与约束验证 |
如果用户需要复制、阅读或提交这个字段的值,通常更适合只读。如果这个字段当前不适用、不能操作,并且不应随表单提交,才考虑禁用。不要为了让控件变灰而随手改成 disabled,否则可能导致提交数据缺失。
只读控件不会因为设置了 required 就自动变成有效的必填校验目标:只读控件不参与浏览器约束验证,required 对它不起到通常的必填约束作用。因此,如果某个值必须满足业务规则,应在服务端验证;如果希望浏览器对用户输入进行必填校验,字段就必须处于可编辑且参与验证的状态。
表单提交时要检查什么
只读值通常仍会包含在表单提交中,但它需要像其他表单字段一样拥有 name,并且所在表单确实被提交。例如:
<input
id="order-code"
name="orderCode"
type="text"
value="ORD-1042"
readonly>
如果控件没有 name,浏览器不会把它作为常规表单字段提交。若控件被禁用,即使有 name,也不会随普通表单提交。使用 JavaScript 构造 FormData 时也要留意:标准表单数据会遵循成功控件规则,而不是简单地收集页面上所有输入框。
只读意味着“用户界面不允许编辑”,不意味着字段值绝对不会变化。脚本可以设置 field.value,开发者工具可以修改页面,客户端请求也可以被构造或篡改。因此,不应依赖只读字段来保护价格、权限、账户编号等敏感或关键数据。
安全边界:前端只读不是权限控制
readonly 只影响浏览器中的交互,不提供身份认证、授权或数据完整性保证。用户可以修改 DOM、直接发起 HTTP 请求,也可能在请求中提交与页面显示不同的值。把字段设为只读,不能阻止有意绕过页面的人提交伪造数据。
服务端应根据可信数据重新计算或核验关键值。例如,商品金额应由服务端根据商品和数量计算;当前用户是否能修改记录,应由服务端检查授权;订单状态变化应由服务端验证允许的状态流转。客户端提交的只读字段可以用于展示或辅助交互,但不能直接作为权限判断或最终业务事实。
若需要把动态内容插入页面,优先使用 textContent 设置文本。不要把不可信输入直接拼接到 innerHTML 中,否则可能引入跨站脚本风险。只读属性本身不会过滤输入,也不会净化 HTML。
常见错误及修正
错误一:把字符串 “false” 当作关闭
field.setAttribute("readonly", "false");
这仍然是只读,因为布尔属性只看是否存在。改成 field.readOnly = false,或者移除属性。
错误二:在不支持 readonly 的控件上使用它
例如给复选框或下拉框添加 readonly,通常不会产生预期效果。先确认控件类型;对不支持只读的控件,考虑禁用、只读文本展示,或调整交互设计。
错误三:状态只在页面加载时设置一次
如果控制条件会变化,却没有监听对应事件,字段状态就会过期。为相关控件注册 change 事件,并在初始化时同步一次状态。若条件还会被其他脚本修改,也要在那些修改逻辑完成后重新调用状态更新函数。
错误四:只改变外观,没有改变交互状态
用 CSS 灰化输入框并不会让它只读。用户仍可能编辑,表单行为也不会因此改变。样式应该作为真实状态的提示,而不是替代 readOnly 属性。
错误五:用 readonly 代替服务端校验
浏览器里的限制可以改善正常用户的操作体验,但不能作为安全边界。只读字段中的金额、用户 ID、角色或审批结果,都需要在服务端重新检查来源和业务规则。
实现时的检查清单
- 确认目标控件支持
readonly;不支持的控件不要依赖该属性。 - 使用
element.readOnly = true或false切换状态,避免用字符串"false"误判。 - 将状态绑定到明确的业务条件,并在条件变化时更新。
- 检查字段是否有
name,以及它是否应该随表单提交。 - 需要禁止交互且不提交时,再评估是否应使用
disabled。 - 不要把只读状态当成数据校验、权限控制或防篡改措施。
- 给状态变化提供清楚的视觉或文本提示,并用真实浏览器测试键盘操作和表单行为。
总的来说,根据条件切换文本框只读状态,核心就是监听条件变化,并把结果写入控件的 readOnly 属性。只读适合“可以查看、不能手动编辑,但仍需要保留在表单数据中”的场景;禁用则表达“当前不可操作”,且通常不会随表单提交。把这一区别、控件适用范围和服务端校验边界处理清楚,就能让表单行为既符合用户预期,也更可靠。



