前言
项目中使用了element-plus,然后el-select组件使用了远程搜索
问题
select可以进行搜索,后端过滤,突然发现点击事件无法触发(除了第一个之外)
过程
- 查看源码,el-select写了几个函数,分别是remote-method,blur事件,初步对照api查看没什么问题,然后记起来blur有些问题,所以就查看了blur函数代码,发现只是简单的重新设置了optionList。
- 重新查看blur代码,感觉可以延迟触发更新optionList代码。尝试下了,发现正常了。
- 阅读源码,调试不了,最后发现是使用了webpack的externals规则。就直接改了重新查看源码
- 调试源码也没发现什么问题(挺正常的,失焦事件,点击事件),最后想到了vue的更新机制。
- 通过对vue的更新机制回想,思考应该式这个过程,但是要理清的化,发现只有dom没有进行复用才有可能触发这个bug。但是我觉得不可能在存在复用的过程中销毁掉dom元素,也没啥必要啊。
- 最后猜想可能是浏览器的机制问题。
- 开始使用简单的demo复现问题,还真是这个。
demo 代码 操作复现:
- 通过操作点击子节点div,发现是可以触发alert的;再次通过input失焦事件,发现是可以正常添加dom元素的;
- 刷新页面开始操作,先让input进入聚焦状态,然后点击第一个子节点,发现新增了一个元素,再发现页面alert了一下,正常现象;
- 刷新页面,重新让input进入聚焦状态,然后点击第二个子节点,发现新增了一个元素,但是没有alert,然后点击第三个子节点,发现是可以alert的。异常现象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- 输入框失去焦点后改变dom节点是否会触发事件 -->
<input type="text" id="input" />
<div id="app">
<div class="child-1">子节点1</div>
<div class="child-1">子节点2</div>
<div class="child-1">子节点3</div>
</div>
</body>
<script>
window.onload = function() {
document.getElementById("input").addEventListener("blur", function() {
const el = document.createElement('div')
el.className = 'child-1'
el.innerText = "子节点"+(document.getElementsByClassName('child-1').length+1)
// 插入节点
document.getElementById('app').insertBefore(el,document.getElementsByClassName('child-1')[1])
});
const domlist = document.getElementsByClassName('child-1');
for(let i=0;i<domlist.length;i++){
domlist[i].addEventListener('click',function(){
alert('===')
})
}
};
</script>
</html>
结果
通过推导以及实验得知2个结果:
- input事件优先级最高。(失焦后先触发函数,再触发点击事件)
- 如果在input失焦后,鼠标点击的地方如果新增了元素,那么原先dom元素便不会触发dom事件。
备注: dom事件的一个很复杂的过程,是屏幕先接收到鼠标点击的位置,然后把位置发送给浏览器线程,然后浏览器线程结合视图,依次触发坐标所在的dom的捕获事件和冒泡事件,一层一层的穿透,又一层一层的冒泡,最后结束。
总结
浏览器的事件原理以及element-plus源码以及vue源码都很重要。
最重要的一点:遇到input的blur事件,如果情况允许的化,可以使用定时器执行代码。