根据el-tooltip封装自适应文本长度的tip

文章目录

组件基于 element uiel-tooltip实现;
el-tooltip本身不支持按照文本长度进行自适应显示; 因此在文本长度较短时弹出tip会显得怪怪的;

1. 思路及原理概述

element ui 中, el-table是实现了单元格的长度自适应的, 使用过程中也比较流畅; 因此, 根据 el-table的源码进行改造;
源码路径: \element-ui\packages\table\src\table-body.js
方法: handleCellMouseEnter(event, row) 以及 handleCellMouseLeave(event)

原理: 通过mouseEntermouseLeave 事件监听元素宽度, 从而计算是否显示 tooltip

2. 引入依赖

import { getStyle } from '@/utils/dom'
import { debounce } from 'throttle-debounce'

说明:

  1. dom.jselement ui中提取, 源码路径: \element-ui\src\utils\dom.js, (看他写得挺复杂的, 就不自己造*了)
  2. throttle-debounce 直接安装即可: npm install throttle-debounce --save

3. 组件实现

<template>
  <div class="self-popover-main">
    <div
      class="self-popover-content"
      @mouseenter="handleMouseEnter"
      @mouseleave="handleMouseLeave"
    >
      <slot />
    </div>
    <el-tooltip
      v-show="popText !== ''"
      ref="tooltip"
      :content="popText"
      :effect="effect"
      :placement="placement"
      :visible-arrow="visibleArrow"
      :popper-class="popperClass"
    />
  </div>
</template>

<script>

import { getStyle } from '@/utils/dom'
import { debounce } from 'throttle-debounce'

// 兼容子节点样式, 参见 controlledClazzList;
// 当子节点样式不存在时, 使用 self-popover-content来进行判断
export default {
  name: 'SelfTooltip',
  props: {
    placement: {
      type: String,
      default: 'top'
    },
    visibleArrow: {
      type: Boolean,
      default: false
    },
    popperClass: {
      type: String,
      default: ''
    },
    effect: {
      type: String,
      default: 'light'
    }
  },
  data() {
    return {
      popText: '',
      controlledClazzList: ['self-tooltip', 'with-popover']
    }
  },
  created() {
    this.activateTooltip = debounce(50, tooltip => tooltip.handleShowPopper())
  },
  methods: {
    handleMouseEnter: function(event) {
      const target = event.target
      // 判断是否text-overflow, 如果是就显示tooltip
      let child = target
      for (const clazz of this.controlledClazzList) {
        const tmp = target.querySelector(`.${clazz}`)
        if (tmp) {
          child = tmp
          break
        }
      }
      let heightFlag = false
      if (child.scrollHeight > child.offsetHeight) {
        heightFlag = true
      }
      // use range width instead of scrollWidth to determine whether the text is overflowing
      // to address a potential FireFox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1074543#c3
      const range = document.createRange()
      range.setStart(child, 0)
      range.setEnd(child, child.childNodes.length)
      const rangeWidth = range.getBoundingClientRect().width // 文本区域宽度
      const padding = (parseInt(getStyle(target, 'paddingLeft'), 10) || 0) +
        (parseInt(getStyle(target, 'paddingRight'), 10) || 0)
      if ((rangeWidth + padding > target.offsetWidth || child.scrollWidth > child.offsetWidth) || heightFlag && this.$refs.tooltip) {
        const tooltip = this.$refs.tooltip
        // TODO 会引起整个 Table 的重新渲染,需要优化
        this.popText = target.innerText || target.textContent
        tooltip.referenceElm = target
        tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none')
        tooltip.doDestroy()
        tooltip.setExpectedState(true)
        this.activateTooltip(tooltip)
      }
    },
    handleMouseLeave: function(event) {
      const tooltip = this.$refs.tooltip
      if (tooltip) {
        tooltip.setExpectedState(false)
        tooltip.handleClosePopper()
        this.popText = ''
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@mixin overflow-hide {
  overflow: hidden;
  text-overflow: ellipsis;
  -o-text-overflow: ellipsis;
  word-break: break-word;
  white-space: nowrap;
}
.self-popover-content {
  @include overflow-hide;
  div {
    @include overflow-hide;
  }
}
</style>

4. 使用示例

<self-tooltip placement="top" effect="light" :visible-arrow="false" popper-class="upload-file-pop">
  <div class="file_name">{{ uploadProcess.name }}</div>
</self-tooltip>

5. 说明

  1. 组件支持自定义适配样式, 只需要在 controlledClazzList中配置即可;
  2. 组件支持无样式匹配(即插槽中的根元素可以不配置controlledClazzList中的样式), 同时 插槽支持 div;
  3. 样式使用了 scss, 可自行翻译成标准 css;
上一篇:课13 key fsm debounce


下一篇:杰理之影响功耗的次要因素一般都与硬件有关【篇】