el-select 单选时,选择后输入框的is-focus状态并没有取消

前两天在封装组件的时候,发现el-select 单选时,选择后输入框的is-focus状态并没有取消,需要手动点其它地方才会取消,于是想着找找为什么
在这里插入图片描述

一、通过调试源码发现,输入框在点击选项后触发blur,紧接着又触发了一次focus

1.状态样式由计算属性"wrapperKls"控制,又与“isFocused”相关,且handleFocus和isFocused都由useFocusController提供

在这里插入图片描述
在这里插入图片描述

2.因此,找到useFocusController的具体实现,加入console.log就能观察到focus触发情况

在这里插入图片描述
查看控制台
在这里插入图片描述

二、有devTools也可以观察到,点击选项后isFocused先变成false,又变成true,实际上还是上方useFocusController中的handleFocus改变了其值

在这里插入图片描述

三、找一下点击选项后发生了什么

深扒一下,干了4件事

ctx.emit(UPDATE_MODEL_EVENT, option.value)	// ctx.emit('update:modelValue', val) 更新双向绑定的值
emitChange(option.value)	// 值改变的情况下调用 ctx.emit('change', val) 触发el-select的change事件
states.visible = false	// 那么这时又发生了什么呢?
setSoftFocus()	/* 看起来像是这个方法导致的
	const _input = input.value || reference.value
    if (_input) {
      _input?.focus()
    }
*/

在这里插入图片描述

四、经过我的排除大法,有两种情况会触发的focus事件

  • 由states.visible改变触发
  • 由setSoftFocus()方法触发

如果想在点击选项后不触发focus,那么就需要同时注释这两行代码才行
在不破坏代码功能的情况下,加入一个方法setSoftBlur和一个prop
用户在单选时如果传入了autoBlur,那么

 const setSoftBlur = () => {
    const _input = input.value || reference.value
    if (_input) {
      _input?.blur()
    }
  }
	/**
     * @description when select one item, click option will let input blur
     */
    autoBlur: Boolean,

在这里插入图片描述
tip:使用setTimeout涉及js任务队列与事件循环,将在下方执行setSoftFocus之后调用setSoftBlur()

五、效果如图

在这里插入图片描述

在这里插入图片描述

总结

前前后后花了两天多,刚开始以为是某个内部组件的事件冒泡导致的,源码拉下来找得头都晕了input、popper、tooltip、focus-trap、useFocusController等等,后面又从头开始,一边写博客一边找,这样清晰很多,再加上联想起focus事件没有冒泡,那么问题就在input身上,只需要弄清点了选项之后,发生了什么,就真相大白了。

上面其实只是找到了一半的原因,除了setSoftFocus方法外,将states.visible改为false也会让input触发focus,如下:
注释掉setSoftFocus()与新加的代码后,延后1秒执行state.visible = false 可以明显看到输入框先blur,在visible变为false后又focus了
在这里插入图片描述

注释了对states.visible的watch也没用
有点不解,搞不清楚为什么visible改变会触发输入框的focus事件,可能是浏览器更底层的一些机制导致的,道行太浅,后面在慢慢看了

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过监听 `change` 事件来实现这个功能。具体实现步骤如下: 1. 给每个 `el-select` 绑定 `change` 事件。 2. 在事件处理函数中,获取当前选中的选项。 3. 遍历所有 `el-select`,将当前选中的选项禁用,并设置其他 `el-select` 中相同选项为禁用状态。 4. 如果取消选中当前选项,则将其解除禁用状态。 下面是一个示例代码,你可以参考一下: ```html <template> <div> <el-select v-model="selected" @change="handleChange"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option> </el-select> <el-select v-model="selected" @change="handleChange"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option> </el-select> </div> </template> <script> export default { data() { return { selected: '', options: [ { label: '选项1', value: '1', disabled: false }, { label: '选项2', value: '2', disabled: false }, { label: '选项3', value: '3', disabled: false }, ], }; }, methods: { handleChange() { // 获取当前选中的选项 const selectedOption = this.options.find(item => item.value === this.selected); // 遍历所有 el-select,禁用当前选中的选项,并设置其他相同选项为禁用状态 this.$nextTick(() => { const selects = document.querySelectorAll('.el-select'); selects.forEach(select => { const options = select.querySelectorAll('li'); options.forEach(option => { if (option.getAttribute('data-value') === selectedOption.value) { option.classList.add('is-disabled'); } if (option.classList.contains('selected')) { const value = option.getAttribute('data-value'); const sameOption = this.options.find(item => item.value === value); if (sameOption && sameOption.value !== selectedOption.value) { option.classList.add('is-disabled'); sameOption.disabled = true; } } }); }); }); }, }, }; </script> ``` 上面的代码中,我们给每个 `el-select` 绑定了 `change` 事件,并在事件处理函数中实现了禁用选项和解除禁用选项的逻辑。当选项状态改变,我们通过遍历所有 `el-select`,找到需要禁用的选项,并设置为禁用状态。如果取消选中当前选项,则将其解除禁用状态。注意,由于 `el-select` 是异步渲染的,所以我们需要在 `$nextTick` 中处理选项状态

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值