点击上传按钮后应使用 insertadjacenthtml 动态插入 type="button" 的取消按钮并保留 form 属性,上传中通过 abortcontroller 中止 fetch 请求,撤销时调用服务端删除接口并统一管理 uploadstate 状态。

点击上传按钮后怎么动态插入取消按钮
上传开始时不能直接用 innerHTML 替换原按钮,否则会丢失 name、data-file-id 等上下文信息,也容易破坏表单关联。正确做法是用 insertAdjacentHTML 在原按钮旁插入新按钮,再显式移除旧按钮。
关键点:
- 新插入的取消按钮必须带
type="button",否则在<form></form>内会触发表单提交,导致 405 错误 - 如果原上传按钮有
form="my-form"属性,取消按钮也要带上,保持 DOM 关联 - 插入后立即调用
requestAnimationFrame触发重绘,避免移动端 UI 闪动
取消按钮点击后为什么不能清空 input[type=file].value
清空 input[type=file].value = '' 只能重置本地选择状态,对已发到服务端的文件毫无作用,还可能让用户误以为“已撤销”,造成数据不一致。
真正该做的事:
- 点击后立即将取消按钮设为
disabled,文字改为「撤销中…」 - 调用服务端删除接口(如
DELETE /api/uploads/{file_id}),传入存在data-file-id或闭包变量里的 ID - 撤销成功后,恢复原始上传按钮(包括
disabled状态、name、value和所有data-属性) - 撤销失败时必须恢复按钮可点击状态,否则用户卡死——常见错误是只处理
fetch().then(),漏掉catch()或 4xx 响应分支
如何让取消操作真正中断正在进行的上传请求
原生 XMLHttpRequest 支持 .abort(),但 fetch 不行。若用 fetch,得靠 AbortController 主动中止。
实操要点:
- 上传前创建
const controller = new AbortController(),把signal传给fetch({ signal }) - 取消按钮点击时调用
controller.abort(),触发fetch的 rejection - 注意:
abort()不会撤回已发出的请求,只让前端停止监听响应;服务端仍需独立实现删除逻辑 - 不要在
catch里重复调用服务端删除——那是撤销按钮的事,不是中断上传的事
为什么撤销按钮常被点两次或失效
根本原因在于状态没统一管理。上传中、上传完成、撤销中、撤销失败……这些状态如果散落在多个变量或 DOM 属性里,很容易不同步。
推荐做法:
- 用一个单一对象(如
uploadState = { status: 'idle', fileId: null, abortController: null })集中存所有状态 - 所有 UI 更新(按钮显隐、文字、禁用)和逻辑判断(能否点击撤销)都基于这个对象读取
- 每次状态变更后,用
Object.assign(uploadState, {...})或直接赋值更新,避免引用残留 - 特别注意:
abortController实例是一次性的,abort()后不可复用,下次上传必须新建
file_id 时用户就点了撤销,此时前端既没 ID 可删,又不能假定上传失败,得靠服务端幂等接口兜底。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











