用vue 自定义指令解决表格td内容元素设置z-index不生效问题

问题描述

在表格内使用表单时,如果表单控件在表格每一行的高度范围内,那么它的错误提示就会被遮挡看不到。(我的UI库是用的iview)
无法显示表单提示内容

这是一直存在的问题,以前的解决方法:

  1. 将表单控件的错误提示距离留出来,把表格每一行撑高。这样表格每两行之间会间隔很大,还可能会出现一行的内容对不齐的问题
  2. 把错误提示放到表单控件里来,这样有时又影响查看内容。

原本想着反正我们是中台系统,这次还是准备用第一种方式继续蒙混过关,但是没想到这次的需求表格是带颜色的,加上颜色第一种方案缺点就太明显了,并且产品又不接受第二种方案,该改还是得要去改啊。

分析

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属性,发现能正常显示在两行表格之间,证明这个方案没问题,可以照这样去做。
调整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>
..省略...

优化

上面代码仍可以优化。

  1. 错误提示虽然会显示、隐藏,但是它的位置是不会变的,而且只要设置了一次它的样式,并不需要再变回到以前的样式。 所以我们不需要在update里去修改,只需要在insert(使用insert时要保证父元素存在)里执行一次绑定修改即可。

  2. 这里要找到3个元素出来修改,其实可以写好一个class,找到父元素<td>,给它加上这个class就可以了

  3. 如果怕有的表单层级一样,还可以传入参数,根据参数递归去找要修改的父元素(这一点我没优化)

...省略...
Vue.directive('tableformerrorposition', {
    
    
  inserted(el) {
    
    
    // 找到<td>元素
    let parentTdEl = el.parentNode.parentNode.parentNode
    parentTdEl.className += ' update-form-item-error-position'
  },
})
...省略...

看看最后效果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BAtodl/article/details/114991562