做前端这些年,我几乎每天都要和 <input> 标签打交道,但说句实话,一直到工作第三年,我都不敢说自己真正"吃透了"HTML里这个最基础的表单元素。<input> 的属性数量虽然不算夸张,但每个属性的行为会因为 type 不同而产生完全不同的表现,浏览器兼容性也各有差异。这篇文章想把 HTML <input> 属性做一个尽量系统、尽量贴近实战的梳理,不是为了让你背属性表,而是帮你理解每个属性解决什么问题、有哪些隐藏的坑。
标题叫"属性大全",但我会按真实使用场景来分组讲,而不是把 MDN 那个巨长的属性列表照抄一遍。读完你会得到三样东西:一套可以直接抄的属性组合思路,一些在项目里反复踩过的兼容性坑,以及一个可以当作实战手册来翻的速查索引。
1. 先理解 input:一个标签半部表单史
1.1 为什么讲 input 不能只背属性表
HTML 表单是所有 Web 应用的入口,而 <input> 又是表单里最核心的交互元素。你可以不用 <select>、不用 <textarea>,但你不可能不用 <input>。它一个标签就承载了文本框、密码框、复选框、单选按钮、文件上传、日期选择、颜色选择、范围滑块等十几类控件形态。
很多人学 <input> 的时候喜欢拿 MDN 属性表去背,今天背明天忘,原因很简单:属性在不同 type 下的行为完全不一样。比如 value 在 text 里是初始文本,在 checkbox 里是提交给后端的值,在 color 里是 #rrggbb 格式的字符串,在 file 里根本就用 value 去预设路径(出于安全原因浏览器不允许)。所以理解属性必须和 type 绑定,脱离 type 谈属性等于耍流氓。
另一个容易被人忽略的点是:<input> 虽然叫"输入",但它不只是给用户输入的,它还承担了大量浏览器原生能力调用的职责。比如 <input type="file" accept="image/*" capture="environment"> 这一行代码就可以直接唤起手机后置相机拍照,不需要你引任何 SDK。这种"原生控件即能力"的思路,是 <input> 在现代 Web 开发中依然不可替代的根本原因。
1.2 type 是总开关,其他属性是精细调节
我习惯把 <input> 的属性分成两个层次:第一层是 type 决定控件的基本形态,第二层是其余属性在某个形态下做精细控制。
type 的取值非常丰富,我这里列一个最常用的分类视角:
| 分类 | type 取值 | 典型用途 |
|---|---|---|
| 文本类 | text, search, email, url, password, tel | 普通文本、搜索、邮件、网址、密码、电话 |
| 数值类 | number, range | 精确数值输入、滑块近似取值 |
| 日期时间类 | date, time, datetime-local, month, week | 各类日期时间选择 |
| 选择类 | checkbox, radio | 复选、单选 |
| 按钮类 | button, submit, reset | 自定义按钮、提交、重置 |
| 文件类 | file | 文件上传 |
| 其他 | color, hidden, image | 颜色选择、隐藏字段、图片提交按钮 |
默认值就是 text,也就是说你什么都不写,浏览器也会给一个单行文本输入框。type 一旦确定,控件的键盘类型、校验规则、提交行为都会跟着变。比如移动端 type="email" 会弹出带 @ 和 . 的键盘,type="number" 会弹出数字键盘,type="tel" 会弹出电话键盘。这种原生体验是任何 JS 模拟控件都很难完全复刻的,也侧面说明了为什么正规的 type 声明那么重要。
type 选对了,剩下的大部分属性只是"要不要加"的问题。选错了,后面写再多属性也救不回来。比如你指望 type="text" 做范围限制,然后写一堆 JS 去挡非法字符,其实直接换成 type="number" 加 min max 更省事。
1.3 一个最小可用 input 需要哪些属性
如果只让我保留三个属性,我会选 type、name、id。
type决定形态。name决定提交给后端的字段名。没有name,表单提交时这个输入框的值就不会被发送,很多新手在这里踩过坑——表单提交后后端收不到数据,查来查去发现是忘了写name。id用于和<label>关联,提升可点击命中区域,这是可用性里最基础的一环。
html复制<label for="username">用户名</label>
<input type="text" id="username" name="username">
id 和 name 看起来都长得像字符串,但不要混用。id 是页面内唯一标识,name 是表单字段名。写 CSS 要用 id,提交数据要用 name。虽然现在常有人用 name 同时兼做样式钩子,但语义上还是要分清。
有了这三个属性,你的 text 输入框就已经能提交数据了。剩下的属性都是在不同业务需求下不断叠加的精细化配置。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 按真实场景分组吃透常用属性
2.1 值域控制组:value、placeholder、readonly、disabled
先看最容易混淆的三个概念:value、placeholder、readonly、disabled。
value 是控件的当前值。对 text 类来说,它表示文本框里显示的初始文本,用户在页面上修改的内容会同步到这个属性(至少浏览器内部状态是这样)。对 checkbox 和 radio 来说,value 是提交给后端的标识值。比如:
html复制<input type="checkbox" name="hobby" value="reading"> 阅读
<input type="checkbox" name="hobby" value="coding"> 编程
用户勾选"阅读"后,提交的 hobby 值就是 reading,而不是什么"阅读"这两个中文。一个最常见的坑是:新手写 checkbox 忘了 value,结果提交时拿到的默认值是 "on",后端根本没法判断用户选了哪个。
placeholder 是占位提示文本,它在输入框没内容的时候显示灰色提示,用户一输入就消失。它不能替代 label,因为当用户输入内容后提示就没了,而且屏幕阅读器对 placeholder 的朗读支持不如 label 完整。如果表单只有 placeholder 没有 label,无障碍评分会很差。补救办法是加 aria-label 或 aria-labelledby。
readonly 和 disabled 是高频混淆点,它们都能让用户没法修改值,但区别很关键:
readonly的值会随表单提交,disabled的值不会。readonly可以被聚焦、可以被复制,disabled不响应任何交互。- 样式上浏览器默认会给 disabled 一个置灰外观,readonly 通常没有明显变化。
html复制<input type="text" value="只能看不能改" readonly>
<input type="text" value="完全禁用" disabled>
如果你的需求是"这个字段只读但数据要传给后端",请用 readonly。如果需求是"条件不满足时不允许提交这个字段",而且你不想在后端收到它,用 disabled。一个实用技巧是:临时禁用后再恢复时,用 JS 改 disabled 属性即可,但注意改回时要重新赋值 value,因为 disabled 期间浏览器不会保留用户可能输入的内容。
2.2 表单关联组:name、form、formaction 系列
name 的作用上面已经讲了,这里重点说 form 属性。
HTML5 允许 <input> 放在 <form> 外面,通过 form="表单ID" 建立关联。这个特性在处理复杂页面布局时非常有用。比如一个弹窗里的提交按钮,在外面某个隐藏表单里收集数据,你不需要把 input 物理移动到 form 内部。
html复制<form id="searchForm" action="/search">
<input type="hidden" name="type" value="article">
</form>
<!-- 这个 input 虽然在表单外面,但提交时也会跟着一起发送 -->
<input type="text" name="keyword" form="searchForm">
同样的逻辑还有 formaction、formenctype、formmethod、formnovalidate、formtarget。这五个属性都是给 submit 按钮使用的,用来覆盖所属 <form> 上的对应属性。典型场景是搜索表单里同时有"搜索"和"导出"两个按钮,一个提交到 A 接口,一个提交到 B 接口:
html复制<form action="/searchList">
<input type="text" name="keyword">
<button type="submit">搜索</button>
<button type="submit" formaction="/export" formmethod="POST">导出</button>
</form>
这样两个按钮走不同的提交地址,但共用同一个输入框,不需要复制表单,也不需要写 JS 动态改 action。
2.3 必填与约束组:required、maxlength、minlength、pattern
这组属性是浏览器原生表单校验的核心。
required 表示必填。只要这个 input 在提交时值为空,浏览器会拦截提交并显示一条提示气泡。注意两个边界情况:
type="checkbox"加required时,要求必须勾选后才能提交,常见于"我已阅读协议"。- 多个
name相同的 radio 只要其中一个加required,就代表该组必须选一个。
maxlength 和 minlength 不是所有 type 都生效,它俩主要作用于 text、search、url、tel、email、password 等文本类控件。maxlength 是硬性限制,用户超过长度后无法继续输入,而且这个限制是在浏览器层面完成的,后端理论上可以信任它,但后端仍然要做长度校验,因为直接构造 HTTP 请求完全可以绕过前端限制。
pattern 是正则校验,它验证的是 value 的整个字符串是否匹配,不是局部匹配,而且匹配的是"是否能在 value 中找到匹配子串"还是"整个 value 是否匹配"?这里有个经典陷阱:pattern="\d{4}" 想限制四位数字,实际 "12345" 也能通过校验,因为正则没有加 ^ 和 $ 锚点。标准规定 pattern 匹配的是整个 value 值的比较,等价于用正则的 .test() 但隐式加了 ^(?:...)$ 效果。实测时最好写完整锚点:
html复制<input type="text" pattern="[1-9][0-9]{4,14}" title="请输入5到15位数字,且首位不能为0">
title 属性在这里不是普通提示,它会作为原生校验失败时提示气泡的补充说明文案。很多浏览器默认的提示是英文的"Please match the requested format",用户根本看不懂,加上 title 之后会显示更友好的描述。
3. 数字类与验证约束的细节
3.1 min、max、step 在 number 和 range 中的表现差异
min、max、step 这三个属性主要服务 number、range、date、time 以及后面几个日期时间类控件。
它们的校验逻辑是"基于步进的合法性"。举个例子:
html复制<input type="number" min="0" max="100" step="10" value="30">
用户手动输入 35,提交时会被判定为非法,因为 (35 - min) / step = 3.5,不是整数。这里有个容易踩的坑:很多浏览器对 number 的 step 校验非常严格,你给一个 step="0.1" 的输入框,用户输入浮点数时可能出现难以察觉的精度问题。比如 step="0.1" 时,(0.3 - 0) / 0.1 = 2.9999999999999996,结果不是整数,导致 0.3 被判为不合法。解决办法有两种:一是把 step 设置得足够小,比如 step="any",二是后端接收后做二次规则校验,不依赖前端的 step 精确统一。
step="any" 是一个实用的特殊值,它表示允许任意小数步进,适合金额、百分比这类不需要固定步长的输入。
range 类型默认的 min 是 0,max 是 100,value 是 50。它不要求用户输入精确值,而是提供一个滑块。滑块的值会显示在 value 中但不显示在界面上,通常你需要配合 JS 把当前值渲染出来:
html复制<input type="range" min="0" max="1000" step="50" value="200" id="priceRange">
<span id="priceOutput">200</span>
<script>
const range = document.getElementById('priceRange');
const output = document.getElementById('priceOutput');
range.addEventListener('input', function () {
output.textContent = this.value;
});
</script>
对 date 和 time 来说,min 和 max 是日期或时间字符串,比如 min="2024-01-01"。这在做日期范围选择时很好用,可以直接用 HTML 属性限制可选范围,不需要引入日期组件库。但要注意:如果你选的某个第三方 UI 库是基于 div 模拟控件的,HTML 的原生属性不会生效,必须走库自己的配置项。
3.2 pattern 正则与 noValidate 的配合
在讲 pattern 的配套逻辑之前,要说明一个机制:HTML5 表单校验是"先前端拦截,后提交"的流程。如果表单里有任何一个控件校验失败,整个表单的 submit 事件都不会触发,自然也不会发起提交。这在绝大多数情况下是符合预期的,但在某些场景下很碍事。
比如注册页有个"确认用户名是否可用"的按钮,用户输入内容后先走 AJAX 校验,再整体提交。如果用户名输入框的 pattern 不满足条件,点击"提交"按钮时直接被浏览器挡住了,你的 AJAX 根本不会执行。这时可以在 <form> 上加 novalidate,关闭浏览器原生校验,然后在 JS 里自己控制校验逻辑;或者只在 submit 按钮上加 formnovalidate,让这个按钮不触发原生校验。
html复制<form action="/register" novalidate>
<input type="text" name="username" pattern="[a-zA-Z0-9_]{4,16}" required>
<button type="submit">注册</button>
</form>
另一种思路是保留原生校验,但用 JS 监听 invalid 事件自定义错误文案:
js复制const input = document.querySelector('input[name="username"]');
input.addEventListener('invalid', function () {
if (this.validity.valueMissing) {
this.setCustomValidity('请输入用户名');
} else if (this.validity.patternMismatch) {
this.setCustomValidity('用户名只能包含字母、数字、下划线,长度4到16位');
}
});
input.addEventListener('input', function () {
this.setCustomValidity('');
});
setCustomValidity('') 必须要在下一次校验前清空,否则表单会一直处于"自定义错误"状态。第一次写的时候很容易漏掉这个清空操作,结果用户改了内容还是提示错误。
3.3 title、aria-label 与无障碍的必要性
<input> 是交互控件,它不是静态展示,所以无障碍属性非常重要。我这里只讲三个最核心的:
title 除了上文说的校验提示,它还能在鼠标悬浮时显示一个原生 tooltip。但建议不要把关键信息只放在 title 里,因为在移动端触摸操作中 title 基本不生效。
aria-label 是给屏幕阅读器朗读的标签。如果出于视觉设计需要隐藏 <label>,可以用 aria-label 替代。优先考虑普通可见的 <label>,因为它对增强点击区域也有帮助。
aria-describedby 可以把一段说明文字关联到输入框,屏幕阅读器会在朗读完 label 之后继续朗读这段描述。对于密码强度提示、输入格式说明这类内容很有效:
html复制<label for="pwd">密码</label>
<input type="password" id="pwd" name="pwd" aria-describedby="pwdHelp">
<span id="pwdHelp">至少8位,包含大小写字母和数字</span>
不要小看这些属性。团队接无障碍改造需求时,最耗时的往往不是布局,而是这些"看不见的"属性有没有补齐。
4. 移动端与高交互场景的高级属性
4.1 inputmode 与 enterkeyhint 的实战价值
移动端没有物理键盘,所以控制软键盘的形态和行为是提升输入体验的关键,而 inputmode 正是为此设计的。
inputmode 和 type 容易混淆,区别在于:type 决定控件的语义和校验规则,inputmode 只决定软键盘的布局,不改变校验。比如你可以用 type="text" 加 inputmode="numeric",既允许用户输入任意文本,又在移动端弹出数字键盘。这个组合非常实用,因为 type="number" 在某些 Android 浏览器上不允许输入 -、. 等字符,导致负数和小数无法输入。
html复制<!-- 更好的手机号输入体验 -->
<input type="text" name="phone" inputmode="tel">
<!-- 更好的验证码输入体验 -->
<input type="text" name="code" inputmode="numeric" maxlength="6" pattern="\d{6}">
inputmode 的常用值:
| 值 | 弹出的键盘类型 | 典型场景 |
|---|---|---|
| text | 默认全键盘 | 普通文本 |
| decimal | 数字键盘,带小数点 | 金额输入 |
| numeric | 纯数字键盘 | 验证码、数量 |
| tel | 电话键盘 | 手机号 |
| 带@和.com的键盘 | 邮箱输入 | |
| url | 带/和.com的键盘 | 网址输入 |
| search | 键盘确认键变"搜索" | 站内搜索 |
enterkeyhint 则是设置键盘右下角"回车键"的文案或图标,比如搜索场景把回车提示改成"搜索",用户按右下角按钮直接触发查询:
html复制<input type="search" enterkeyhint="search">
支持的常见值有 enter、done、go、next、previous、search、send 等。这个属性在表单多字段跳转时也很有用:最后一个输入框设置 enterkeyhint="done",用户按完成后自动收起键盘。
这两个属性不是所有移动端浏览器都支持得完美,尤其在 iOS 和 Android 的 webview 中表现不一致。但作为渐进增强,该写还是要写,写错了也不至于出功能问题。
4.2 autocomplete 的值列表与自动填充踩坑
autocomplete 是一个经常被轻视的属性,它管的是浏览器自动填充。合理利用能极大提升表单填写效率,不合理使用则可能让用户被自动填充的老数据坑到。
常用值包括:
off:关闭自动填充on:允许,但不指定具体字段类型(浏览器根据 name/id 猜)name、given-name、family-name等:姓名分段email、tel、street-address、postal-code:联系信息current-password、new-password:密码one-time-code:短信验证码(iOS 上可以自动从短信中提取)
密码框这里有个非常值得注意的地方:如果是注册/修改密码页,填新密码的输入框最好写成 autocomplete="new-password"。很多浏览器看到 type="password" 就默认走"已保存密码"的自动填充逻辑,导致用户根本没法输入新密码,老是被自动填充成某个旧密码。加上 new-password 之后能有效规避这个问题。
html复制<input type="password" name="new_password" autocomplete="new-password">
验证码输入框建议 autocomplete="one-time-code",在 iOS Safari 上短信里的验证码会自动高亮提示填入,体验提升明显。
还有一个容易被忽略的场景:隐藏的搜索框或页面内嵌 iframe 表单,如果不希望浏览器弹出自动填充,可以设置 autocomplete="off"。但说实话,现在 Chrome 和 Safari 对 off 的支持已经越来越"看心情"了,如果遇到自动填充顽固生效,还得靠 JS 动态改 name 属性等"土办法"做兜底。不过这种方案会牺牲原生体验,不建议作为默认做法。
4.3 list 与 datalist 组合的输入补全
list 属性可以让普通输入框关联一个 <datalist>,实现"可输入 + 有建议选项"的效果。这比 <select> 灵活,比写全套自动补全组件轻量:
html复制<input type="text" name="city" list="cityList">
<datalist id="cityList">
<option value="北京"></option>
<option value="上海"></option>
<option value="广州"></option>
<option value="深圳"></option>
</datalist>
用户点击输入框后,Chrome 和大多数浏览器会以下拉列表形式展示建议项。用户可以直接选,也可以继续输入自由文本。
几个实用细节:
datalist的id要和list属性的值严格一致。- 在 Firefox 中,
datalist对type="date"也有支持,可以给出日期建议值。 - 如果对
type="range"使用list,可以在滑块的几个刻度位置显示刻度点,样式由浏览器默认处理,定制能力有限。 - 移动端对
datalist的支持并不统一,部分 Android WebView 不显示建议列表,但不会影响正常输入,可以放心做渐进增强。
5. 一些容易忽略但能救命的属性细节
5.1 autofocus、tabindex 与页面加载体验
autofocus 可以让页面加载后自动聚焦到某个输入框。搜索页、登录页用一个 autofocus 就能让用户少点一次,体验提升很直观:
html复制<input type="search" name="q" placeholder="搜索关键词" autofocus>
但要注意几个问题:一是一个页面只能有一个 autofocus,多个同时写只会生效第一个;二是移动端上自动唤起键盘可能遮挡弹出层或让页面跳动,所以弹窗里的输入框不要加 autofocus,改成弹窗打开后再调用 focus() 方法更好控。
tabindex 是键盘导航顺序的关键。默认情况下,表单控件的 Tab 顺序是按 DOM 顺序来的。如果布局导致视觉顺序和 DOM 顺序不一致,需要通过 tabindex 调整。但不要乱调,否则容易让用户键盘操作变得顺序混乱。一个常见场景是弹窗内用 tabindex="0" 让原本不可聚焦的元素获得焦点,但注意弹窗关闭后要恢复。
这里插一个 readonly 和 tabindex 的组合案例:只读文本字段如果还希望用户可以 Tab 到它并使用方向键选中文字,保留默认 tabindex 即可;如果完全不希望用户 Tab 到它,可以加 tabindex="-1"。
5.2 disabled 与 readonly 的再次辨析(配合 JavaScript)
前文说了两者的提交差异,这里补一个 JS 交互层面的差异。
disabled 的 input 在 JS 中读取 .value 时依然可以拿到值(只要之前有值),所以通过 JS 读取是没问题的。但它的值不会出现在 FormData 中,也不会被 FormData 收集。
js复制const formData = new FormData(document.getElementById('myForm'));
// disabled input 的值不在 formData 里
如果你在提交前想临时禁用某个输入框防止用户修改,但后端又需要这个字段,不要抬手就 input.disabled = true,把状态收紧成 readonly 更安全。
另一个容易被忽略的坑是:给 disabled 输入框通过 JS 设置 .value 后,界面虽然显示了新值,但表单提交时依然不会带上它。所以如果你想"修改按钮点击后提交一个隐藏字段的值",应该把这个字段做成 readonly 而不是 disabled。或者干脆不用 <input>,直接在 JS 里给 FormData 追加字段。
5.3 file 输入的 multiple、accept、capture
文件上传是前端高频需求。multiple 控制多选,accept 限制文件类型,capture 在移动端唤起相机或录音。
html复制<input type="file" accept="image/*" capture="environment">
capture 的两个取值:
capture="user":唤起前置摄像头,适合自拍、人脸识别。capture="environment":唤起后置摄像头,适合拍文档、扫码。
如果省略 capture,在多数移动端上会弹出"拍照/相册/文件"的选择菜单,让用户自己选。注意 capture 在 iOS Safari 上表现比较稳定,在部分 Android 机型上即使设置了也不一定唤起相机,而是直接跳转文件选择器,这是厂商 ROM 的行为差异,不是你代码的问题。
accept 的写法除了 MIME 类型,也可以写文件扩展名:
html复制<input type="file" accept=".jpg,.jpeg,.png">
但严格说,accept 只是一个"建议性"过滤,用户仍然可以切到"所有文件"来选择不匹配的类型,所以前端过滤 + 后端校验才是完整方案。很多实际项目里,前端用 accept 做了类型限制后,后端就不再校验了,这是隐患。改后缀名的文件、伪造 Content-Type 的请求,都能绕过前端限制。
5.4 容易被忽略的 hidden 和 image
不要觉得 hidden 很基础就跳过,它有几个细节值得说明:
type="hidden"不会在页面上显示任何内容,但它的value会随表单提交。- 它不参与焦点、不触发校验、不接受
readonly、disabled样式等大多数交互属性。 - 它天然是"不可见但可提交"的数据载体,适合放 token、用户 ID、操作类型等元信息。
type="image" 则是用图片作为提交按钮。它可以配合 src 和 alt,效果等价于 <input type="submit"> 加上一张背景图。点击图片提交时,浏览器还会额外提交 x 和 y 两个坐标参数(点击位置相对图片左上角的坐标),这在做类似"地图点击坐标上报"的玩法时会有用。
html复制<input type="image" src="submit-btn.png" alt="提交">
这种方式比用 <button> 加背景图的写法语义更准,但样式灵活性差一点,所以现在团队里用的人不多,但遇到老项目维护时还是要认得。
5.5 表单校验 API 与 CSS 状态选择器
既然讲属性,就不能不提校验状态的 CSS 钩子。很多前端团队花大力气去写 JS 校验逻辑、手动添加红色边框,但其实浏览器原生提供了非常实用的状态选择器:
:valid:校验通过:invalid:校验失败:required:有 required 属性:optional:没有 required 属性:in-range/:out-of-range:针对有 min/max 的数值和日期输入:placeholder-shown:占位提示正在显示
一个常见的实战写法是:默认输入框不加边框色,提交时才用 :invalid 标红。如果不用 JS 配合,很难做到"用户还没输入时不要一片红,只有提交后才标红"的效果:
css复制input:invalid {
border-color: red;
}
这样写的话,页面一加载,所有必填项就会因为"空值不匹配 required"而变红,体验很差。比较靠谱的做法是:利用 form 的 novalidate 或者等用户交互后再加一个 class,通过 .was-validated input:invalid 来触发样式。CSS 也只能做到"状态变化时设置样式",真正的交互节奏还是要 JS 来控制。
校验 API 也值得了解:每个 input DOM 对象都有 .validity 对象,包含 valueMissing、patternMismatch、tooLong、rangeOverflow、stepMismatch 等字段。这些字段能精确告诉你校验失败的具体原因,比泛泛的"invalid"状态好用得多:
js复制const validity = input.validity;
if (validity.valueMissing) {
// 空值
} else if (validity.patternMismatch) {
// 正则不匹配
} else if (validity.rangeOverflow) {
// 超过 max
}
6. 属性速查表:一张表帮你定位问题
有些时候记不清属性具体行为,建议直接查表定位。这张表是实战中我真正用过的属性集合,按功能归类,方便快速对照:
| 功能类别 | 属性 | 主要作用 | 注意事项 |
|---|---|---|---|
| 控件类型 | type | 决定输入控件形态 | 默认 text |
| 数据提交 | name | 表单字段名 | 无 name 不提交 |
| 当前值 | value | 初始值和提交值 | checkbox/radio 默认值不同 |
| 占位提示 | placeholder | 灰色提示文字 | 不能替代 label |
| 只读状态 | readonly | 不可修改,可提交 | 可聚焦、可复制 |
| 禁用状态 | disabled | 不可交互,不提交 | 值不出现在 FormData |
| 必填 | required | 空值不允许提交 | radio 组内只需一个 |
| 长度限制 | maxlength / minlength | 字符长度约束 | 后端仍需校验 |
| 正则校验 | pattern | 按正则校验值 | 注意锚点和 title 提示 |
| 数值范围 | min / max / step | 数值和日期范围约束 | step 用 any 可允许任意小数 |
| 自动补全 | autocomplete | 浏览器自动填充 | 新密码框用 new-password |
| 键盘模式 | inputmode | 移动端软键盘类型 | 不改变校验语义 |
| 回车键文案 | enterkeyhint | 移动端键盘右下角提示 | 搜索、发送、下一项 |
| 联动建议 | list | 关联 datalist 提供建议 | 可自由输入 |
| 自动聚焦 | autofocus | 页面加载后自动聚焦 | 每页最多一个 |
| 表单关联 | form | 让 input 关联外部表单 | 配合 formaction 等 |
| 文件上传 | accept / multiple / capture | 文件类型、多选、相机唤起 | 后端必须再校验文件类型 |
| 无障碍 | aria-label / aria-describedby | 屏幕阅读器朗读 | 能不用 hidden label 就不用 |
| 隐藏字段 | type="hidden" | 不可见但随表单提交 | 可用于 token 等元信息 |
这张表不能替代完整文档,但它覆盖了平时开发里 90% 的 <input> 使用场景。遇到"这个字段为什么提交不了""为什么老是被自动填充""移动端键盘不对"这类问题,先回来查表定位,比看控制台猜半天效率高得多。
我在实际项目里养成的一个习惯是:写 <input> 之前,先问自己四个问题——这个字段要提交给后端吗?要不要原生校验?移动端要弹出什么键盘?要不要支持无障碍朗读?只要把这四个问题过一遍,该写哪些属性基本就清楚了。HTML 的属性并不需要背,它更像是一套"你只要想到需要什么能力,就能找到对应属性"的工具箱。用多了自然就记牢了。
