overflow-popover实现(用于文本超出显示...的情况)

1,419 阅读3分钟

前言

作为一个前端靓仔,平时日常开发时经常会涉及到 popover(组件 | Element) 这个组件。

一般都是在B端列表处使用会比较多,比如在商品名称或有提示性icon的地方,使用popover来展示完整信息。 image.png


但是我们都知道,普通的popover是不会判断当前内容是否超出隐藏了!如下:

图一: image.png
图二: image.png

图一内容已经超出了,所以我们应该使用popover去展示完整内容。
图二当前宽度已经示完整内容,所以没必要使用popover。

目前的使用情况下,无论是否超出都会直接显示,这显然是体验不好的。
因此基于popover的基础上,我设计了overflow-popover。

下面附上可直接使用的源码和组件文档。
需要的朋友直接去拿

代码实现

效果如下:

动画9.gif

通过上面的效果图可以看到:
鼠标移上超出隐藏的内容时———popover组件展示。
鼠标移到未隐藏内容时——popover组件不显示。

核心原理

一、设置插槽

popover的内容可以通过插槽去设置

  <el-popover >
    <template slot="reference">
    </template>
  </el-popover>

修改一下改成:

  <el-popover >
    <template slot="reference">
        // 在el-popover的插槽中插入一个div,并提供一个默认插槽
        <div ref="reference">
            <slot/> 
        </div>
    </template>
  </el-popover>

将el-popover原来的slot中默认写入一个div,通过div下slot实现插入数据的功能。

这样的好处的div的宽高会被内容所撑开,而我们通过getBoundingClientRect()方法可以获得这个div的实际宽高,通过比较实际宽高和设置的最大宽度,就可以知道是否应该禁用popover。

二、设置移入移出事件

在div处设置鼠标移入移出的事件

  <el-popover >
    <template slot="reference">
        // 在el-popover的插槽中插入一个div,并提供一个默认插槽
        <div
            @mouseenter="checkIsShowPopover"
            @mouseleave="checkIsShowPopover"
            ref="reference">
            <slot/> 
        </div>
    </template>
  </el-popover>

在触发鼠标移入移出时,我们可以做对这个div宽的判断

三、通过移入移出事件判断是否禁用popover

针对宽度判断是否禁用popover

// checkIsShowPopover方法
checkIsShowPopover() {
    // 最大宽度
    const maxWidth = 180
    // 实际宽度
    const { width } = this.$refs.reference.getBoundingClientRect();
    if( width < maxWidth ) {
        // 说明内容还没出现...
        // 禁用popover
    } else {
        // 放开popover
    }
}

通过这个方法就能实现当前的div宽度是否达到极限,是否应该禁用popover

四、针对多行,超出隐藏...的处理

上面介绍了针对一行最大宽度的处理,但有时候会有多行超出隐藏的需求。(比如超出2行的部分隐藏)
同上:(针对高度判断是否禁用popover
值得注意的是 this.$refs.reference.scrollHeight; 可以获得这个div应该渲染的实际高度

可以通过比较getBoundingClientRect()的height属性和this.$refs.reference.scrollHeight判断是否已经超出最大高度

// checkIsShowPopover方法
checkIsShowPopover() {
    // 真实高度
    const realHeight = this.$refs.reference.scrollHeight
    // 实际宽度
    const { height } = this.$refs.reference.getBoundingClientRect();
    // 实际高度大于或者等于真实高度 说明还没超出
    if( height >= realHeight ) {
        // 禁用popover
    } else {
        // 放开popover
    }
}

完整代码(需要的直接拿就行)

<template>
  <el-popover :disabled="disabled || overflowPopoverDisabled" v-bind="$attrs">
    <template slot="reference">
      <div
        :class="
          lineNum == 1
            ? 'overflow-popover-hidden'
            : 'overflow-popover-line-hidden'
        "
        ref="reference"
        :style="`max-width: ${maxWidth}px;${
          line != 1 ? `-webkit-line-clamp: ${lineNum}` : ''
        }`"
        @mouseenter="checkIsShowPopover"
        @mouseleave="checkIsShowPopover"
      >
        <slot />
      </div>
    </template>
  </el-popover>
</template>

<script>
export default {
  inheritAttrs: false,
  name: "overflowPopover",
  data() {
    return {
      overflowPopoverDisabled: false,
    };
  },
  props: {
    // 是否禁用overflowPopover
    disabled: {
      type: Boolean,
      default: false,
    },
    // 是否进行最大宽度检测 (不做最大宽度检测时,overflowPopover相当普通的Popover组件)
    isCheckMaxWidth: {
      type: Boolean,
      default: true,
    },
    // 超出隐藏的宽度
    maxWidth: {
      type: Number,
      default: 180,
    },
    // 针对多行,超出隐藏...的处理
    lineNum: {
      type: Number,
      default: 1,
    },
  },
  methods: {
    checkIsShowPopover() {
      // 通过获取div节点的getBoundingClientRect属性,拿到div的实际宽度和高度
      const { width, height } = this.$refs.reference.getBoundingClientRect();
      // 多行的情况下,判断高度不用处理宽度
      if (this.lineNum !== 1) {
        // scrollHeight 对应的是实际应该渲染的高度,通过比较二者判断是否显示popover
        const realHeight = this.$refs.reference.scrollHeight;
        this.overflowPopoverDisabled = height >= realHeight;
      } 
      // 只有一行的情况下(无需处理高度)
      else if (this.isCheckMaxWidth) {
        // 开启最大宽度检测时,比较实际的宽度和我们设置传入的maxWidth。
        // 如果没有满足实际小于传入,说明这时候已经出现...隐藏了
        this.overflowPopoverDisabled = width < this.maxWidth;
      }
    },
  },
};
</script>

<style scoped>
.overflow-popover-hidden {
  display: inline-block;
  overflow: hidden;
  align-items: center;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: normal;
}
.overflow-popover-line-hidden {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  vertical-align: middle;
  word-break: break-all;
  line-height: normal;
  cursor: pointer;
}
</style>

代码文档(overflowPopover组件文档)

Props

参数名类型必填默认值描述
maxWidthnumber180最大宽度,超出部分展示省略号,内容超出时指针悬停展示popover
lineNumnumber1处理多行省略的情况
isCheckMaxWidthbooleanfalse是否进行最大宽度检测 (不做最大宽度检测时,overflowPopover相当普通的Popover组件)
disabledbooleanfalse是否禁用

示例

<overflowPopover
      width="180"
      style="display: inline-flex"
      placement="top-start"
      trigger="hover"
      :content="text"
      :maxWidth="120"
      ><span>{{ text }}</span></overflowPopover
    >

实际效果

动画11.gif

注意

组件使用了透传,其他的props和elment的Popover props一致