表单、表格字段,输入完毕后立即点击【保存】,导致数据未更新就被保存

发布于:2025-07-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

表单、表格字段,输入完毕后(还没失去焦点)立即点击【保存】按钮,同时触发onChange和onClick事件,导致保存的是onChange之前的数据

问题背景

在这里插入图片描述
输入表格上的 数量字段后,此时还没有失焦(如果失焦则会触发onChange事件,通过公式 数量 * 单价 = 金额,自动计算出金额),立即点击【保存】按钮,此时按理说保存接口入参应该是重新计算后的金额,但是现实是数量是更新后的金额,但是金额却是计算前的错误的金额;

问题分析

表单、表格字段,输入完毕后(还没失去焦点)立即点击【保存】按钮,同时触发onChange和onClick事件,导致保存的是onChange之前的数据。

  • JavaScript 是单线程的,看似同时触发,其实不一定 “同时执行”,可能出现 click(保存)在 change 之前或过程中执行 的情况,导致数据未更新就被保存。

change 事件的触发时机:通常在输入框失焦(blur)或值改变且失去焦点时触发(如文本输入框在 blur 时触发,下拉框选择后立即触发)。
点击保存按钮时,输入框会先失焦(触发 blur),进而触发 change 事件;同时按钮的 click 事件也会触发。
若 change 事件包含异步操作(如联动计算、接口请求),click 事件(保存)可能在异步操作完成前就执行,导致数据未更新。

  • 输入完毕后立即点击【保存】会触发四个事件: onchange、onblur、onfocus、onclick,浏览器中,这些事件的触发遵循 “焦点变化优先于点击” 的原则,具体顺序如下

onfocus(输入框获取焦点时已触发) → onblur(输入框失去焦点) → onchange(值改变且失焦时) → onfocus(按钮获取焦点,可选) → onclick(按钮点击)。
(特殊:select 的 onchange 在选择后立即触发,早于 onblur)

  • onchange 包含异步操作,onclick(保存)中的同步代码,先执行,导致组织保存数据是未更新的数据,然后执行onChange,最后调用保存接口;

问题解决

1、场景简单的,直接使用异步任务队列即可,把onClick中的代码放入setTimeout中,这样,先走onChange的异步任务,再走onClick的异步任务;
2、场景复杂的,比如onChange时调用了接口等复杂操作,则需要结合使用状态锁;

// 以 Vue 为例
data() {
  return {
    isChanging: false, // 标记 onchange 是否在执行(含异步)
  };
},
methods: {
  // 处理 onchange 事件(含异步)
  async handleChange() {
    this.isChanging = true;
    try {
      await this.asyncOperation(); // 异步联动计算等
    } finally {
      this.isChanging = false; // 无论成功失败,释放状态
    }
  },

  // 处理 onclick 保存事件
  async handleSave() {
    // 等待 onchange 执行完毕
    if (this.isChanging) {
      await new Promise(resolve => {
        const check = setInterval(() => {
          if (!this.isChanging) {
            clearInterval(check);
            resolve();
          }
        }, 50);
      });
    }
    // 执行保存
    await this.saveData();
  }
}

网站公告

今日签到

点亮在社区的每一天
去签到