问题描述
在表格内使用表单时,如果表单控件在表格每一行的高度范围内,那么它的错误提示就会被遮挡看不到。(我的UI库是用的iview)
这是一直存在的问题,以前的解决方法:
- 将表单控件的错误提示距离留出来,把表格每一行撑高。这样表格每两行之间会间隔很大,还可能会出现一行的内容对不齐的问题
- 把错误提示放到表单控件里来,这样有时又影响查看内容。
原本想着反正我们是中台系统,这次还是准备用第一种方式继续蒙混过关,但是没想到这次的需求表格是带颜色的,加上颜色第一种方案缺点就太明显了,并且产品又不接受第二种方案,该改还是得要去改啊。
分析
1. z-index
像这种被遮挡的内容,第一时间想到的就是设置z-index提升层级,但是无论我怎么设置,z-index都等于9999了也不生效,真是令人头秃。
在百般搜索后,看到有人说: “表格的z-index不管怎么设置,<td>
的层级都是高于<td>
的内容元素的层级”。
那么我这设置z-index的方案是彻底宣布失败了。
2. position
不过这倒是令我想通了日期选择器的下拉部分为什么能正常显示。
可以看到日期选择器的选择部分是生成在<body>
里的,相对定位自然是相对于<body>
,那么可以浮在表格上就很正常。(其实我之前想拿日期选择器做参考已经看过了,但是一直没回过味来,现在才一下想通)
让我重新生成元素这是不可能的,但是使用定位又给了我一个思路:
如果我把错误提示的定位参考从它的父元素改到<body>
,那是否也能如 日期选择器的选择部分那样通过计算到<body>
的位置而展示呢?
好的!那就先在浏览器上修改尝试一下。
先取消错误提示(.ivu-form-item-error-tip
)父元素(.ivu-form-item-content
)的绝对定位,再修改.ivu-form-item-error-tip
的top、left属性为0,方便查找。这时.ivu-form-item-error-tip
找到设置了绝对定位的.ivu-table-wrapper
,已经改变位置了。
再修改一下.ivu-form-item-error-tip
的top属性,发现能正常显示在两行表格之间,证明这个方案没问题,可以照这样去做。
修改代码,测试效果
要操作多个元素,且方法都是一样的,那么用vue的自定义指令是最方便的。(vue自定义指令不多做赘述,参考官网:https://cn.vuejs.org/v2/guide/custom-directive.html)
定义一个指令v-tableformerrorposition
,在错误提示出现时修改对应的几个元素的样式,并注册到vue中
...省略...
Vue.directive('tableformerrorposition', {
update: function(el) {
const classList = el.getAttribute('class').split(' ')
// 判断是否出现错误提示
if (classList.includes('ivu-form-item-error')) {
// 这是ivu-form-item-content
let contentEl = el.children[0]
// 这是ivu-form-item-error-tip
let errTipEl = contentEl.children[1]
// 在浏览器测试时找到`<table>`去设置定位还是太麻烦,可以尝试找到父级表格`<td>`去修改定位,这里就直接找到<td>元素
let parentTdEl = el.parentNode.parentNode.parentNode
// ivu-form-item-content取消绝对定位
contentEl.setAttribute('style', 'position:static;')
// <td>加上绝对定位
parentTdEl.setAttribute('style', 'position:relative;')
// ivu-form-item-error-tip调整相对定位距离
errTipEl.setAttribute('style', 'top:44px;')
}
},
})
...省略...
在页面上使用指令
...省略...
<Form ref="chargForm" :model="chargForm" :label-width="120">
<Table class="table-list" :columns="chargColumns" max-height="600" :data="chargForm.list">
<template slot-scope="{ index }" slot="firstMonths">
<FormItem
label=""
:label-width="0"
:prop="'list.' + index + '.firstMonths'"
v-tableformerrorposition
>
<Input v-model="xxx" />
</FormItem>
</template>
</Table>
</Form>
..省略...
优化
上面代码仍可以优化。
-
错误提示虽然会显示、隐藏,但是它的位置是不会变的,而且只要设置了一次它的样式,并不需要再变回到以前的样式。 所以我们不需要在update里去修改,只需要在insert(使用insert时要保证父元素存在)里执行一次绑定修改即可。
-
这里要找到3个元素出来修改,其实可以写好一个class,找到父元素
<td>
,给它加上这个class就可以了 -
如果怕有的表单层级一样,还可以传入参数,根据参数递归去找要修改的父元素(这一点我没优化)
...省略...
Vue.directive('tableformerrorposition', {
inserted(el) {
// 找到<td>元素
let parentTdEl = el.parentNode.parentNode.parentNode
parentTdEl.className += ' update-form-item-error-position'
},
})
...省略...
看看最后效果: