网页表单这玩意儿,天天用,但一堆隐藏的坑让人头大。比如用JS提交表单,明明写了阻止默认行为,它还是嗖地一下发出去了;或者想手动打开日期选择器,怎么点都没反应。最近浏览器悄悄加了一批新API,专治这些疑难杂症。下面直接上硬货,看完就能用。
提交翻车
很多小伙伴用.submit()方法直接提交表单,结果发现页面刷新了,preventDefault()压根没起作用。就像下面这段代码,点击按钮后表单照飞不误,控制台啥也没打印:
const testForm = document.forms[0];
testForm.addEventListener('submit', function(ev) {
ev.preventDefault();
console.log('想拦没拦住');
});
document.querySelector('.sendBtn').addEventListener('click', function() {
testForm.submit();
})为啥会这样?因为.submit()触发的是原生提交动作,不会冒泡submit事件,所以事件监听器里的代码等于白写。更离谱的是,就算输入框加了required属性,空着也能直接发出去,HTML校验直接被无视。
请求提交
requestSubmit()就是来填坑的。这货会老老实实触发submit事件,还会先跑一遍HTML表单校验。所有字段通过考验,submit监听器里的代码才能执行。把上面例子里的.submit()换成.requestSubmit(),效果立竿见影:
document.querySelector('.sendBtn').addEventListener('click', function() {
testForm.requestSubmit(); // 这回校验和事件都安排了
})Safari 16刚补上这个功能,现在所有主流浏览器都齐活了。写代码时注意:requestSubmit()可以传一个参数,指定哪个按钮触发的提交(比如form.requestSubmit(confirmBtn)),但不传参数时,会模拟第一个提交按钮的行为。要是表单里连个提交按钮都没有,调用这个方法会直接报错。
按钮来源
表单里经常放多个提交按钮,比如“保存草稿”和“正式发布”。以前后端只能靠不同的name或value来区分是哪个按钮被点。现在submit事件里多了个submitter属性,可以直接拿到触发提交的那个按钮元素,贼方便。
<form>
<button name="action" value="draft" type="submit">存草稿</button>
<button name="action" value="publish" type="submit">发出去</button>
</form>document.forms[0].addEventListener('submit', function(event) {
event.preventDefault();
const whichBtn = event.submitter;
console.log(whichBtn.value); // 输出 'draft' 或 'publish'
console.log(whichBtn.name); // 输出 'action'
// 给被点的按钮加个旋转动画类
whichBtn.classList.add('loading-spinner');
})这个submitter属性在Safari 15.4之后全平台支持。有个细节要注意:如果表单不是通过按钮提交(比如直接按回车),submitter的值是undefined。所以在代码里最好加个判断:const btn = event.submitter || someDefaultButton;,避免翻车。
数据拦截
formdata事件是个老演员了,但直到Safari 15才全面普及。它的核心玩法是:在表单数据打包发送之前,可以偷偷往里塞东西或者改掉现有值。相当于给数据流装了个拦截器。
const targetForm = document.querySelector('form');
targetForm.addEventListener('formdata', function(ev) {
ev.formData.append('csrf_token', 'xyz789');
ev.formData.set('user_id', '10086');
});
targetForm.addEventListener('submit', function(ev) {
ev.preventDefault();
const fd = new FormData(targetForm);
// 假装用fetch发出去...
});不管是普通HTML提交,还是手动new FormData(),只要表单触发了数据组装,formdata事件就会跑起来。这个特性特别适合用在自定义组件里,比如一个封装的日期选择器,可以把自己的值通过formdata.append悄悄加进去,省得在HTML里塞一堆隐藏input。
选择器唤醒
showPicker()是专门给日期、颜色、文件这类输入框准备的。以前想调出颜色选择盘,直接.click()就行,但日期选择器死活点不出来。现在用.showPicker()一招通杀。
// 日期选择器
const dateInput = document.querySelector('input[type="date"]');
dateInput.addEventListener('click', function() {
this.showPicker(); // 点一下就把日期面板弹出来
});
// 颜色选择器也兼容
const colorPicker = document.querySelector('input[type="color"]');
document.querySelector('.colorBtn').addEventListener('click', function() {
colorPicker.showPicker(); // 和.click()效果一样,但更语义化
});支持的类型有:date、month、week、time、datetime-local、color、file。Chrome 99、Firefox 101、Safari 16之后都能用。注意showPicker()只能在用户手势(比如点击事件)里调用,不能在页面加载时直接跑,否则会被浏览器拦截。
表单禁用
inert属性是个新来的,它能一键让整个表单变成“只读+不可聚焦”状态。和fieldset加disabled不同,inert可以直接怼在<form>标签上,里面所有输入框、按钮、链接都点不了,也切不过去焦点了。
<form inert>
<input name="username" value="张三">
<button type="submit">提交</button>
</form>上面这个表单看着还在,但实际上啥也动不了。辅助技术会把它当成aria-hidden="true",屏幕阅读器直接跳过。如果想加点视觉提示,自己写CSS:
form[inert] {
opacity: 0.5;
pointer-events: none;
}这个属性特别适合做表单提交后的防重复操作,或者临时冻结整个区域。不过inert不会阻止表单里面的脚本执行,比如setInterval回调还是能跑,只是用户没法交互。
| 方法/属性 | 适用版本 | 主要用途 |
|---|---|---|
| requestSubmit | Safari16+全支持 | 触发校验+submit事件 |
| submitter | Safari15.4+全支持 | 获取触发提交的按钮 |
| formdata | Safari15+全支持 | 拦截/追加表单数据 |
| showPicker | Chrome99+等 | 程序化打开选择面板 |
| inert | 现代浏览器 | 一键禁用区域交互 |
上面这些新特性,每个都能解决实际开发中碰到过的糟心问题。特别是requestSubmit和showPicker,以前得写一堆hack代码才能模拟,现在原生API一把梭,代码量直接砍半。
