JavaScript中new FormData未调用自定义getter的问题排查
问题分析
FormData在构造时,并不会读取你通过Object.defineProperty覆盖的value属性getter,而是直接读取浏览器原生维护的表单控件内部值——这就是为什么你手动调用input.value能拿到正确结果,但FormData返回null的原因。
解决方案
方案1:拆分显示与提交元素(最推荐)
既然你的需求是「显示值≠提交值」,最稳妥的方式是用两个元素分离职责:
- 新增一个隐藏input,设置正确的
name属性(对应表单提交的字段名),专门存储实际提交值; - 用另一个元素(比如禁用的input、span)展示选中的显示文本,这个元素不需要
name,不参与表单提交; - 当下拉选中项变化时,同步更新隐藏input的
value和显示元素的文本。
示例代码:
<!-- 隐藏的提交用input --> <input type="hidden" name="Item1.ShippingInfo.StoreOptions.OtherLocationId" id="hidden-location-id"> <!-- 显示用的元素 --> <input type="text" disabled id="display-location" placeholder="选择位置">
// 选中项变化时的逻辑 function onSelectOption(option) { // 更新隐藏input的提交值 document.getElementById('hidden-location-id').value = option.actualValue; // 更新显示文本 document.getElementById('display-location').value = option.displayText; }
这种方式完全规避了自定义属性的兼容性问题,FormData会自动读取隐藏input的正确值。
方案2:监听formdata事件自动修正
如果不想修改组件结构,可以监听表单的formdata事件——这个事件会在new FormData(form)时触发,你可以在事件回调里手动替换对应字段的值:
shippingForm.addEventListener('formdata', (e) => { const targetInput = shippingForm.querySelector('#Item1_ShippingInfo_StoreOptions_OtherLocationId'); if (targetInput) { const actualValue = targetInput.dataset.inputValue ?? targetInput.value; // 替换FormData中的对应字段 e.formData.set('Item1.ShippingInfo.StoreOptions.OtherLocationId', actualValue); } });
之后每次创建FormData时,都会自动修正该字段的值,无需手动调用set。
方案3:同步原生value与dataset值
如果你坚持使用自定义getter,可以在更新dataset.inputValue的同时,同步更新原生的value存储(注意不要触发你的自定义setter,或者在setter里处理逻辑):
// 选中项变化时,同时更新dataset和原生value function onSelectOption(actualValue, displayText) { this.DropDownInputTarget.dataset.inputValue = actualValue; // 直接调用原生setter更新内部值,避免触发自定义逻辑 nativeInputSetter.call(this.DropDownInputTarget, actualValue); // 单独更新显示文本(比如修改input的显示内容,这部分需要你自己实现) this.DropDownInputTarget.placeholder = displayText; // 或者用其他方式展示 }
这样FormData读取原生value时就能拿到正确的实际值,同时自定义getter依然能返回预期结果。
内容的提问来源于stack exchange,提问作者Jeff B
相关产品推荐
相关产品推荐

