<input type="file">是唯一原生、可靠、无障碍友好的图片选择入口,必须用<label>包裹或for关联实现点击触发,禁用display:none隐藏,accept仅作UI提示,校验需JS完成,预览优先用URL.createObjectURL()并及时revoke。

input type="file" 是唯一原生、可靠、无障碍友好的图片选择入口,其他所有“图片选择器”都是对它的封装或模拟——绕开它,基本等于自己造轮子还踩坑。
为什么必须用 <label> 包裹而不是 JS 调用 click()
直接 input.click() 在现代浏览器里大概率失效:
- iOS Safari(16+)静默拒绝非用户手势直接触发的
click(),没报错也没反应 - Chrome 120+ 加强了调用链校验:上层事件如果不是
pointerdown或touchstart原生触发,会被拦截 - 屏幕阅读器无法识别“伪装按钮”,
label关联才是语义正确解法
正确写法是:<label for="img-upload">选择图片</label><input type="file" id="img-upload" accept="image/*">。这样既保留可访问性,又不用 JS 拦截事件。
accept="image/*" 只是 UI 提示,校验必须在 JS 里做
accept 不阻止用户选中非图片文件,比如把 report.txt 改名成 report.jpg,file.type 就可能是 text/plain 或空字符串。
- 优先用
file.type.startsWith('image/')判断,兼容 JPEG/PNG/WebP/AVIF - 当
file.type === ''时,可 fallback 到扩展名检查(如.heic、.heif),Android Chrome 对 HEIC 常识别为application/octet-stream - 别写
file.type === 'image/jpeg'—— 这会漏掉所有非 JPEG 图片
预览大图别用 FileReader.readAsDataURL()
选一张 10MB 的 PNG,readAsDataURL() 会生成 ~13MB 的 base64 字符串塞进内存,低端安卓机容易 OOM,滚动卡顿明显。
立即学习“前端免费学习笔记(深入)”;
- 首选
URL.createObjectURL(file):返回临时 blob URL,零拷贝、内存占用极低 - 记得在不需要预览时调用
URL.revokeObjectURL(url),否则 blob 引用残留导致内存泄漏 - iOS Safari 对 WebP/HEIC 的
createObjectURL支持不稳定,此时再 fallback 到FileReader - 注意:blob URL 不能直接上传,服务端不认;preview 和 upload 必须走两套路径
多图选择要加 multiple,但别忽略 files 是 FileList 不是数组
<input type="file" multiple accept="image/*"> 启用多选后,event.target.files 返回的是只读的 FileList,不是 Array。
- 不能直接调用
.map()、.filter()等数组方法,得先转:Array.from(event.target.files) - 移动端长按/框选多图支持因系统而异,iOS Safari 多图选择仍需用户逐个点选(除非用
capture,但那会禁用文件选择) - 如果后续要上传,
FormData.append('images', file)可多次调用,后端接收为数组即可



















