【elementui】解决el-select组件失去焦点blur事件每次获取的是上一次选中值的问题

目录

【问题描述】

【问题摘要】

【分析问题】

【完整Test代码】

【封装自定义指令】


↑↑↑↑↑↑↑↑↑↑↑↑ 不想看解决问题过程的可点击上方【封装自定义指令】目录直接跳转获取结果即可~~~

【问题描述】

一位朋友遇到这么一个开发场景:在表格里面嵌入el-select 组件,每次修改值后,失去焦点时将修改值提交后台保存,但是发现在el-select 组件失去焦点时,blur 事件的evt.target.value值总为前一次选择的值;

【问题摘要】

1、el-select组件失去焦点时需要获取组件当前值而不是上一次的值;

2、el-select组件切换选项时不提交后台,只有当组件失去焦点后才提交当前值;

【分析问题】

一刚开始以为是下拉框收起动画延时问题导致内部value 值被延时修改,所以就在blur事件里延时获取evt.target.value,貌似没问题了

javascript 复制代码
blurHandler(evt){
	setTimeout(() => {
		console.log(evt.target.value);
	},250)
},

随后又发现通过**@blur** 绑定事件只会触发一次,好吧那就加修饰符吧:@blur.capture.native,好像解决只触发一次的问题了

html 复制代码
<el-select v-model="value" @blur.capture.native="nativeBlurHandler" ref="select">
	<el-option
	  v-for="item in options"
	  :key="item.value"
	  :label="item.label"
	  :value="item.value">
	</el-option>
</el-select>

但是随之而来的问题就是,每次切换取值后都会触发一次blur 事件,这可不是我们想要的,这不变成change事件了吗?继续深挖!

那就打印一下this.refs.select** 看看吧,找到**this.refs.select.$data, 看里面有定义啥变量没,发现了如下变量:

javascript 复制代码
log(eventName){
	let {
		createdSelected,
		inputHovering,
		isOnComposition,
		isSilentBlur,
		menuVisibleOnFocus,
		softFocus,
		visible
	} = this.$refs.select.$data;
	
	console.table([
		{
			name: eventName,
			value: '',
		},
		// {
		// 	name: 'createdSelected',
		// 	value: createdSelected
		// },
		{
			name: 'inputHovering',
			value: inputHovering
		},
		// {
		// 	name: 'isOnComposition',
		// 	value: isOnComposition
		// },
		{
			name: 'isSilentBlur',
			value: isSilentBlur
		},
		// {
		// 	name: 'menuVisibleOnFocus',
		// 	value: menuVisibleOnFocus
		// },
		// {
		// 	name: 'softFocus',
		// 	value: softFocus
		// },
		{
			name: 'visible',
			value: visible
		},
	])
}

通过反复测试发现只有inputHovering,isSilentBlur,visible 这三个变量与el-select组件focus,blur事件有关联:

通过上图可以发现,只有最下方的失去焦点事件才是我们想要的,此时**visible==false,isSilentBlur==true,**正好满足我们的判断条件,于是blur事件改造成这样了:

javascript 复制代码
nativeBlurHandler(evt){
	console.log('nativeBlurHandler', {...this.$refs.select});
	let {
		isSilentBlur,
		visible
	} = this.$refs.select;
	if( isSilentBlur  && !visible ) {
		console.log('提交', this.$refs.select.selected.currentValue);
	}
	this.log('blur事件');
},

又因为value取值从evt.target.value 获取的不是实时的,也就是值不正确,继续深挖,发现this.$refs.select.selected.currentValue就是我们想要的正确的值,于是乎,问题就解决了!测试demo完整代码如下:

【完整Test代码】

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<!-- import CSS -->
		<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
	</head>
	<body>
		<div id="app">
			<el-select v-model="value" placeholder="请选择" @blur="blurHandler" @blur.capture.native="nativeBlurHandler" ref="select">
			    <el-option
			      v-for="item in options"
			      :key="item.value"
			      :label="item.label"
			      :value="item.value">
			    </el-option>
			</el-select>
		</div>
	</body>
	<!-- import Vue before Element -->
	<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
	<!-- import JavaScript -->
	<script src="https://unpkg.com/element-ui/lib/index.js"></script>
	<script>
		let selectElm;
		new Vue({
			el: '#app',
			data: function() {
				return {
					options: [{
					  value: '黄金糕',
					  label: '黄金糕'
					}, {
					  value: '双皮奶',
					  label: '双皮奶'
					}, {
					  value: '蚵仔煎',
					  label: '蚵仔煎'
					}, {
					  value: '龙须面',
					  label: '龙须面'
					}, {
					  value: '北京烤鸭',
					  label: '北京烤鸭'
					}],
					value: ''
				}
			},
			methods: {
				focusHandler(evt){
					this.log('focus事件');
				},
				blurHandler(evt){
					setTimeout(() => {
						console.log(evt.target.value);
					},250)
				},
				nativeBlurHandler(evt){
					console.log('nativeBlurHandler', {...this.$refs.select});
					let {
						isSilentBlur,
						visible
					} = this.$refs.select;
					if( isSilentBlur && !visible ) {
						console.log('提交', this.$refs.select.selected.currentValue);
					}
					this.log('blur事件');
				},
				log(eventName){
					let {
						createdSelected,
						inputHovering,
						isOnComposition,
						isSilentBlur,
						menuVisibleOnFocus,
						softFocus,
						visible
					} = this.$refs.select.$data;
					
					console.table([
						{
							name: eventName,
							value: '',
						},
						// {
						// 	name: 'createdSelected',
						// 	value: createdSelected
						// },
						{
							name: 'inputHovering',
							value: inputHovering
						},
						// {
						// 	name: 'isOnComposition',
						// 	value: isOnComposition
						// },
						{
							name: 'isSilentBlur',
							value: isSilentBlur
						},
						// {
						// 	name: 'menuVisibleOnFocus',
						// 	value: menuVisibleOnFocus
						// },
						// {
						// 	name: 'softFocus',
						// 	value: softFocus
						// },
						{
							name: 'visible',
							value: visible
						},
					])
				}
			}
		})
	</script>
</html>

【封装自定义指令】

想到给组件添加ref 实际并不是那么好用且麻烦,所以我又将其封装成了指令,指令代码及用法如下,大家随取随用哈**↓↓↓↓↓↓**

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<!-- import CSS -->
		<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
	</head>
	<body>
		<div id="app">
			<el-row>
				<el-col :span="24">
					<el-select v-select-blur="selectBlurHandler" v-model="value">
						<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
						</el-option>
					</el-select>
				</el-col>
			</el-row>
		</div>
	</body>
	<!-- import Vue before Element -->
	<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
	<!-- import JavaScript -->
	<script src="https://unpkg.com/element-ui/lib/index.js"></script>
	<script>
		Vue.directive('select-blur', {
			bind: function(el, binding, vnode) {
				const selectComponent = vnode.componentInstance;
				if( selectComponent.$options.name === 'ElSelect' ) {
					selectComponent.$watch('visible', (n,o) => {
						const {
							isSilentBlur,
							visible,
						} = selectComponent;
						if(isSilentBlur && !visible) {
							selectComponent.handleBlur();
						}
					})
					selectComponent.$on('blur', () => {
						let {
							visible,
							selected: {
								currentValue
							}
						} = selectComponent;
						if (!visible) {
							binding?.value && typeof binding.value === 'function' && binding.value(currentValue);
                            selectComponent.blur();    //处理页面频繁聚焦失焦导致重复触发blur事件的问题
						}
					}, true);
				}
			},
			unbind: function(el, binding, vnode) {
				const selectComponent = vnode.componentInstance;
				if( selectComponent.$options.name === 'ElSelect' ) {
					selectComponent.$off('blur');
				}
			}
		});

		const app = new Vue({
			el: '#app',
			data: function() {
				return {
					options: [{
						value: '黄金糕',
						label: '黄金糕'
					}, {
						value: '双皮奶',
						label: '双皮奶'
					}, {
						value: '蚵仔煎',
						label: '蚵仔煎'
					}, {
						value: '龙须面',
						label: '龙须面'
					}, {
						value: '北京烤鸭',
						label: '北京烤鸭'
					}],
					value: ''
				}
			},
			methods: {
				selectBlurHandler(val) {
					console.log('提交', val);
				},
			}
		})
	</script>
</html>
相关推荐
万少10 分钟前
万少用9个AI工具,帮朋友完成了一个"不可能"的项目
前端
小小小小宇12 分钟前
Vue `import` 为什么可以异步加载
前端
WMYeah17 分钟前
【无标题】
前端·rust·抽奖程序·跨平台抽奖程序
Unbelievabletobe18 分钟前
免费外汇api的响应时间在不同时段下的波动分析
大数据·开发语言·前端·python
大哥,带带弟弟28 分钟前
Grafana 前端嵌入与 JWT 鉴权实战
前端·grafana
小小小小宇28 分钟前
前端 V8 引擎垃圾回收机制与内存问题排查
前端
前端老石人40 分钟前
CSS 值定义语法
前端·css
sheeta19981 小时前
Vue 前端基础笔记
前端·vue.js·笔记
小小小小宇1 小时前
GitLab + GitLab Runner + Qiankun 微前端 + Nginx + Node 中间件 前端开发机从零搭建 CI/CD 全流程
前端
前端那点事1 小时前
别再写垃圾组件!Vue3 如何设计「真正可复用」的高质量通用组件
前端·vue.js