文章目录
组件基于
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'
说明:
-
dom.js
从element ui
中提取, 源码路径:\element-ui\src\utils\dom.js
, (看他写得挺复杂的, 就不自己造*了) -
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. 说明
- 组件支持自定义适配样式, 只需要在
controlledClazzList
中配置即可; - 组件支持无样式匹配(即插槽中的根元素可以不配置
controlledClazzList
中的样式), 同时 插槽支持div
; - 样式使用了
scss
, 可自行翻译成标准css
;