先记住这个答案
一张表单可以有多个提交按钮。表单上的 action 和 method 是默认值,具体提交按钮可用 formaction、formmethod 为这次提交覆盖目标与方法。比如“保存草稿”可发到草稿接口,“发布”走正式接口。按钮必须真正发起提交,覆盖才参与这次请求。
- 覆盖属性写在提交按钮上
formaction改提交目标formmethod改请求方法- 只对实际提交者生效
两条操作路径不用复制整个表单
文章编辑页的字段是同一组,只是不同按钮的后续处理不同。把两套字段复制成两个 <form>,容易出现校验与状态不一致。按钮级覆盖让一张表单保留两个清晰的出口,后端仍需对两个接口分别做权限与数据校验。
<form action="/publish" method="post">
<label for="title">标题</label>
<input id="title" name="title" required>
<button type="submit" formaction="/drafts">保存草稿</button>
<button type="submit">正式发布</button>
</form>点击草稿按钮时使用 /drafts,点击发布按钮时使用表单默认 /publish。formmethod 同样可覆盖方法,但是否使用 GET 还要看请求是否适合放入 URL;保存内容通常用 POST。
提交者身份很关键
键盘回车、脚本调用提交 API、点击不同按钮,可能产生不同提交者。若脚本需要明确选择一条按钮级覆盖路径,应通过符合提交者语义的方式发起,例如调用 requestSubmit(button),而不是随意调用不会带上同一行为的旧式提交方法。
按钮不在 <form> 内但通过 form 属性关联时,也可以作为该表单的提交者。覆盖属性不会修改 DOM 上表单的原始 action,下一次用户选择另一按钮,仍会按那次的提交者重新计算。
例如同一编辑器有“预览”和“发布”按钮。按钮上的 formmethod="get" 可以让预览走查询请求,发布继续沿用表单的 method="post";覆盖的是被点击的那个提交按钮,不是永久修改整个表单。脚本若调用 form.requestSubmit(previewButton),也能按预览按钮的提交配置走;直接 form.submit() 则没有这个提交者语义,还会跳过通常的校验和 submit 事件。
容易答错的地方
- 认为按钮覆盖会永久改写表单属性
- 它仅对这一次由该按钮触发的提交有效。
- 在
type="button"上写formaction期待自动发送 - 普通按钮不触发提交,需要真正的提交按钮或显式脚本流程。
面试官还会怎么问?
怎样让草稿跳过必填校验?
可在草稿提交按钮上设置 formnovalidate,再由草稿接口处理不完整数据。
表单外的提交按钮能覆盖吗?
可以,只要通过 form 正确关联目标表单,并作为提交者触发提交。
脚本怎样指定使用哪个按钮的覆盖?
可用 form.requestSubmit(button),让该按钮作为提交者进入正常提交流程。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。