天天看点

根据el-tooltip封装自适应文本长度的tip1. 思路及原理概述2. 引入依赖3. 组件实现4. 使用示例5. 说明

文章目录

  • 1. 思路及原理概述
  • 2. 引入依赖
  • 3. 组件实现
  • 4. 使用示例
  • 5. 说明
组件基于

element ui

el-tooltip

实现;

el-tooltip

本身不支持按照文本长度进行自适应显示; 因此在文本长度较短时弹出tip会显得怪怪的;

1. 思路及原理概述

element ui

中,

el-table

是实现了单元格的长度自适应的, 使用过程中也比较流畅; 因此, 根据

el-table

的源码进行改造;

源码路径:

\element-ui\packages\table\src\table-body.js

方法:

handleCellMouseEnter(event, row)

以及

handleCellMouseLeave(event)

原理: 通过

mouseEnter

mouseLeave

事件监听元素宽度, 从而计算是否显示

tooltip

2. 引入依赖

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

说明:

  1. dom.js

    element 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

    ;