前端mousedown事件focus后突然被调用blur的思考

370 阅读2分钟

先说一下背景:

用的vue框架,在table里面有一栏备注,点击后出现输入框,失去焦点后保存。点击后需要自动聚焦,当前聚焦,点击下一行的时候,下一行也需要自动聚焦。

之前是用div+contenteditable来写的,但是觉得只有一栏编辑,在数据里面多加一个字段来切换div和input感觉代码量会少一点,而且contenteditable限制字数和其他条件比较麻烦(给自己挖坑了)

挖坑历程: 刚开始的思路是,每一行增加一个isEdit字段来控制渲染input还是div,originData字段来存储当前的值(取消的时候需要还原),div绑定click事件将isEdit改成true,异步nextTick聚焦input(采取异步的原因是dom要在re-render之后才会出现);inpit绑定blur事件触发更新以及将isEdit改成false

此时触发事件的顺序是(理想):

点击第一行:第一行click->isEdit改变触发的re-render->第一行foucs

点击第二行: 第一行bulr->isEdit改变触发的re-render->第二行click(这个无法触发,后面都无法执行)->第二行focus->isEdit改变触发的re-render

由于bulr的时候第一行input会突变成div,这个时候,由于第一行高度变动,第二行也跟着变了,但是点击位置还是原先那里,导致无法触发div的点击事件。必须要等re-render渲染完成之后再点一下才能触发div点击事件。

于是,我想把点击事件提前,之前绑定的是click,我改成了mousedown。第二行先执行mousedown,然后再触发第一行的blur事件

此时触发事件的顺序是:

点击第二行(理想):第二行mousedown->isEdit改变触发的re-render->第二行foucs->第一行blur->isEdit改变触发的re-render

但是实际情况是:第二行mousedown->isEdit改变触发的re-render->第二行foucs->第二行bulr->第一行blur->isEdit改变触发的re-render

为什么第二行会莫名其妙自己触发blur呢???

关键在本文第一个标粗的异步nextTick聚焦input那里

监听的事件都是宏任务,鼠标点击会依次触发 mousedown->blur->点击地方focus->mouseup->click

我在mousedown的微任务nextTick里写了input.focus(),所以完整的事件顺序对照上面是:

mousedown宏任务(第二行mousedown->isEdit改变触发的re-render,div替换为input->第二行foucs->)->blur(第二行blur->isEdit改变触发的re-render,input替换为div)->点击地方focus(div的focus)->mouseup->click

关键的地方是mousedown会触发blur,而失去焦点会让input变回div

解决办法:

将input.focus()放进setTimeou就行了,就是这么简单!setTimeout的宏任务会在事件的宏任务之后执行,也就是在mousedown的blur->foucs之后在执行