日常记录#4 二次封装element的toolTip,在文字超出宽度时展示toolTip,支持rowNum:超出rowNum行后显示省略号

<template>
  <div ref="tooltip-parent" class="text-tooltip-wrap">
    <el-tooltip
      :effect="effect"
      :disabled="hideToolTip"
      :content="content"
      :placement="placement"
    >
      <div>
        <div class="show-row-list-wrap" v-if="rowNum > 1">
          <div v-for="(item, index) in showAllStrArr" :key="index" class="row-item-wrap">{{ item }}</div>
        </div>
        <div ref="content-body" class="content-body-wrap" :class="hideToolTip ? '' : 'over'">
          {{ overFlowContent || '' }}
        </div>
      </div>
    </el-tooltip>
  </div>
</template>
 // 二次封装element的toolTip,在文字超出宽度时展示toolTip,支持rowNum:超出rowNum行后显示省略号
<script>
export default {
  name: 'TextToolTip',
  props: {
    effect: {
      type: String,
      default: 'dark'
    },
    content: {
      type: String, // 需要展示的文本
      default: ''
    },
    placement: {
      type: String,
      default: 'top'
    },
    observeNode: {
      type: Element,
      default: null
    },
    stopObserver: {
      type: Boolean,
      default: false
    },
    rowNum: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      hideToolTip: true,
      mutationObserver: null,
      changeArr: ['fontSize', 'fontFamily', 'fontWeight'],
      retryNum: 0,
      parentOffsetWidth: 0,
      parentStyle: {},
      overFlowContent: '',
      showRowNum: 1,
      showAllStrArr: []
    }
  },
  mounted() {
    this.showRowNum = this.rowNum
    this.overFlowContent = this.content
    this.getContentStyle()
    this._checkWidth()
    const { stopObserver } = this
    if (!stopObserver) {
      this._intervalCheck()
    }
  },
  beforeDestroy() {
    const { mutationObserver } = this
    if (!mutationObserver) {
      return
    }
    mutationObserver.disconnect()
    this.mutationObserver = null
  },
  beforeUnmounted() {
    const { mutationObserver } = this
    if (!mutationObserver) {
      return
    }
    mutationObserver.disconnect()
    this.mutationObserver = null
  },
  methods: {
    _intervalCheck() {
      const parent = this.$refs['tooltip-parent']
      if (!parent) {
        return
      }
      const MutationObserver = window.MutationObserver || window.webkitMutationObserver || window.MozMutationObserver
      this.mutationObserver = new MutationObserver(mutations => {
        this._checkWidth()
      })
      // 默认检查组件的parentNode的style变化
      let { observeNode = null } = this
      if (!observeNode) {
        observeNode = parent.parentNode
      }
      this.mutationObserver.observe(observeNode, {
        childList: true, // 观察目标子节点的变化,是否有添加或者删除
        attributes: true, // 观察属性变动
        attributeFilter: ['style'],
        attributeOldValue: true,
        characterDataOldValue: true,
        subtree: true // 观察后代节点
      })
    },
    getContentStyle() {
      const { parentOffsetWidth, parentStyle } = this
      if (parentOffsetWidth > 0) {
        return parentStyle
      }
      const parent = this.$refs['tooltip-parent']
      if (!parent) {
        return {}
      }
      const styles = window.getComputedStyle(parent)
      const { offsetWidth } = parent
      this.parentOffsetWidth = offsetWidth
      const { font, letterSpacing, fontSize } = styles
      this.parentStyle = {
        font,
        letterSpacing,
        fontSize
      }
      return this.parentStyle
    },
    getELeWidth(str) {
      const { font, letterSpacing, fontSize } = this.getContentStyle()
      const textWrap = document.createElement('i')
      textWrap.setAttribute('style', `font: ${font}; letterSpacing: ${letterSpacing}`)
      const textNode = document.createTextNode(str)
      textWrap.appendChild(textNode)
      const bodyEle = document.querySelector('body')
      bodyEle.appendChild(textWrap)
      const totalWidth = textWrap.offsetWidth
      bodyEle.removeChild(textWrap)
      return totalWidth
    },
    _splitWord(str) {
      let initialStr = ''
      const { fontSize } = this.getContentStyle()
      const { parentOffsetWidth } = this
      let initialIndex = ~~(parentOffsetWidth / fontSize)
      initialStr = str.substring(0, initialIndex)
      let childW = this.getELeWidth(initialStr)
      if (childW > parentOffsetWidth && initialIndex > 0) {
        do {
          --initialIndex
          initialStr = str.substring(0, initialIndex)
          childW = this.getELeWidth(initialStr)
        } while (childW > parentOffsetWidth && initialIndex > 0)

      } else if (childW < parentOffsetWidth) {
        do {
          ++initialIndex
          initialStr = str.substring(0, initialIndex)
          childW = this.getELeWidth(initialStr)
        } while (childW < parentOffsetWidth && initialIndex < str.length)
        if (initialIndex < str.length) {
          --initialIndex
        }
      }
      return initialIndex
    },
    _checkWidth() {
      let { retryNum, showRowNum, showAllStrArr } = this
      const contentStr = this?.['content'] || ''
      const parent = this.$refs['tooltip-parent']
      const totalChildWidth = this.getELeWidth(contentStr)
      const content = this.$refs['content-body']
      if (totalChildWidth > 0 && parent.offsetWidth > 0 && totalChildWidth >= parent.offsetWidth) {
        if (showRowNum <= 1) {
          this.hideToolTip = false
        } else {
          const splitIndexArr = [0]
          let currentEndIndex = 0

          while (showRowNum > 1 && currentEndIndex < contentStr.length) {
            const index = this._splitWord(contentStr.substring(splitIndexArr?.[splitIndexArr.length - 1]))
            currentEndIndex += index
            splitIndexArr.push(currentEndIndex)
            --showRowNum
          }

          if (splitIndexArr.length) {
            const splitStrArr = []
            for (let i = 0; i < splitIndexArr.length - 1; i++) {
              const current = splitIndexArr[i]
              const next = splitIndexArr[i + 1]
              const str = contentStr.substring(current, next)
              splitStrArr.push(str)
            }
            const lastStr = contentStr.substring(splitIndexArr[splitIndexArr.length - 1])
            const lastStrWidth = this.getELeWidth(lastStr)
            this.overFlowContent = lastStr
            if (lastStrWidth >= parent.offsetWidth) {
              this.hideToolTip = false
            }
            if (splitStrArr.length) {
              this.showAllStrArr.splice(0, showAllStrArr.length, ...splitStrArr)
            }
          }
        }
      }
      if (content.offsetWidth === 0 && parent.offsetWidth === 0) {
        const { stopObserver } = this
        if (!stopObserver) {
          setTimeout(() => {
            if (retryNum < 3) {
              ++retryNum
              this.retryNum = retryNum
              this._checkWidth()
            }
          }, 20)
        }
      }
    }
  }
}
</script>
<style scoped lang="scss">
@mixin overEllipse {
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.text-tooltip-wrap{
  .content-body-wrap{
    display: inline;
    white-space: nowrap;
    &.over{
      display: block;
      @include overEllipse
    }
  }
}
</style>
<style>
.el-tooltip__popper{
  max-width: 400px;
}
</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值