懒的写文案,我就直接上代码了, 解决了内存泄露的问题,文档在最下方!还在使用vue2+element-ui的直接贴过去用哈,记得点个赞赞~~~
MyTooltip 组件为二次封装的组件
- 使用示例
// 1. 超出一行自动省略,且提示
<my-tooltip :rows="1"> 这是一段文本内容,这是一段文本内容 </my-tooltip>
// 2. 超出两行自动省略,且提示
<my-tooltip :rows="2"> 这是一段文本内容,这是一段文本内容 </my-tooltip>
// 3. 这里是自定义内容
<my-tooltip content="这里是自定义内容"> 这是一段文本内容,这是一段文本内容 </my-tooltip>
// 4. 这里是插槽自定义内容
<my-tooltip content="这里是自定义内容">
<template #content>
<div>
#content插槽
<br>
换行
</div>
</template>
这是一段文本内容,这是一段文本内容
</my-tooltip>
// 5. 点击按钮,控制tootlip显隐(需要定义`tooltipVisible` 变量
<el-button @click="tooltipVisible = !tooltipVisible">点击这里</jl-button>
<my-tooltip v-model="tooltipVisible">这是一段文本内容,这是一段文本内容</my-tooltip>
- 组件代码
<template>
<span
class="my-tooltip"
ref="textRef"
:style="{
'-webkit-line-clamp': rows,
display: block ? '-webkit-box' : '-webkit-inline-box',
}"
:class="{
'is-visible': !rows,
}"
@click="handleClick"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave">
<slot></slot>
<el-tooltip
v-if="showPopper"
ref="tooltipRef"
effect="dark"
:class="{ 'is-one-row': rows === 1 }"
:popper-class="internalPopperClass"
:content="hasContentSlot ? undefined : content || internalContent"
:disabled="isDisabled"
:enterable="enterable"
:placement="placement"
:visible-arrow="false">
<template v-if="hasContentSlot" #content>
<slot name="content"></slot>
</template>
</el-tooltip>
</span>
</template>
<script>
export default {
name: 'MyTooltip',
inheritAttrs: false,
props: {
value: { type: Boolean, default: false },
rows: { type: Number, default: 0 },
block: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
enterable: { type: Boolean, default: true },
content: { type: String, default: '' },
placement: { type: String, default: 'top' },
popperClass: { type: String, default: '' },
hideOnClick: { type: Boolean, default: false },
},
watch: {
async isHovering(newVal) {
if (newVal) {
this.showPopper = true;
}
await this.$nextTick();
const popper = this.$refs.tooltipRef;
const reference = this.$refs.textRef;
if (!popper) return;
if (newVal) {
if (reference) {
popper.referenceElm = reference;
popper.popperJS && (popper.popperJS._reference = reference);
}
// 监听为了销毁
this.showPopperUnwatcher = this.$watch(
() => popper.showPopper,
(isShowNewVal) => {
this.showPopper = this.internalValue = isShowNewVal;
if (isShowNewVal) {
//
} else {
this.unwatchShowPopper();
}
}
);
popper.updatePopper && popper.updatePopper();
popper.show && popper.show();
} else {
popper.hide && popper.hide();
// this.unwatchShowPopper();
}
},
async internalValue(newVal) {
if (newVal) {
this.handleMouseEnter();
} else {
this.handleMouseLeave();
}
},
},
computed: {
internalValue: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value);
},
},
internalPopperClass() {
return ['custom-tooltip', this.popperClass].filter(Boolean).join(' ');
},
isDisabled() {
return this.disabled || (!this.isOverflow && !!this.rows);
},
hasContentSlot() {
return !!this.$slots.content;
},
},
data() {
return {
isOverflow: false,
internalContent: '',
textEl: null,
isHovering: false,
showPopper: false,
showPopperUnwatcher: null,
};
},
methods: {
getContent() {
this.textEl = this.$refs.textRef;
if (!this.textEl) return;
this.internalContent = this.textEl.innerText;
},
handleMouseEnter() {
this.getContent();
this.checkOverflow();
if (this.isDisabled) return;
this.isHovering = this.internalValue = true;
},
async handleMouseLeave() {
this.isHovering = false;
if (!this.enterable) {
this.showPopper = false;
}
},
checkOverflow() {
if (!this.textEl) return;
this.isOverflow = this.textEl.scrollWidth - 1 > this.textEl.clientWidth || this.textEl.scrollHeight - 1 > this.textEl.clientHeight;
},
unwatchShowPopper() {
if (this.showPopperUnwatcher) {
this.showPopperUnwatcher();
this.showPopperUnwatcher = null;
}
},
handleClick() {
if (this.hideOnClick) {
this.showPopper = false;
}
this.$emit('click');
},
},
beforeDestroy() {
// 组件销毁时清理 watcher,防止内存泄漏
this.unwatchShowPopper();
},
};
</script>
<style lang="scss" scoped>
.my-tooltip {
max-width: 100%;
word-break: break-all;
vertical-align: bottom;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
overflow: hidden;
&.is-visible {
overflow: visible;
}
&.is-one-row {
white-space: nowrap;
}
}
</style>
<style lang="scss">
.el-tooltip__popper {
&.custom-tooltip {
z-index: 99999 !important;
max-width: 400px;
max-height: 300px;
overflow-y: auto;
margin: 4px 0;
.popper__arrow {
display: none;
}
}
}
</style>
Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| value / v-model | 控制 tooltip 显示与隐藏(受控) | boolean | true / false | false |
| rows | 控制省略行数 | number | — | 0 |
| content | 显示的内容 | string | — | 默认插槽中的内容 |
| block | 是否块级元素(占一整行) | boolean | true / false | false |
| enterable | 鼠标是否可进入到 tooltip 中 | boolean | true / false | false |
| disabled | 禁用 | boolean | true / false | false |
| placement | 位置 | string | top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end | top |
| hide-on-click | 点击时是否隐藏 tooltip | boolean | true / false | false |
Slot
| name | 说明 |
|---|---|
| content | 自定义tooltip提示内容 |