C端没有引用任何第三方UI库,网上找没有合适的,就决定自己封装一个tooltip,有需要的话,用就完事了~ 示例地址
用法
import tooltip from '@/directives/tooltip';
export default {
...
directives: {
tooltip,
},
...
}
<h1
v-tooltip="{
text: '嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿',
placement: 'top',
}"
>
{{ msg }}
</h1>
<h1
v-tooltip="{
html: 'Hello !<br /><b>Vue</b> in CodeSandbox!',
placement: 'top',
}"
>
xxxxx
</h1>
代码
/* eslint-disable max-len */
import Vue from 'vue';
import './tooltip.less';
// 提示框的id,每执行一次bind加1
let tooltipId = 1;
export default Vue.directive('vktooltip', {
bind(el, binding) {
const value = binding.value;
const privateTooltipId = tooltipId;
// 给提示框打标机,便于unbind的时候移除元素
el.setAttribute('data-tooltip-id', privateTooltipId);
// 处理入参
const DEFAULT_BEHAVIER = 'hover';
const DEFAULT_PLACEMENT = 'top';
let text;
let behavier;
let placement;
let html;
if (typeof value === 'string') { // 传入为字符串,默认top、hover
text = value;
behavier = DEFAULT_BEHAVIER;
placement = DEFAULT_PLACEMENT;
} else {
text = value.text;
html = value.html;
behavier = value.behavier ?? DEFAULT_BEHAVIER;
placement = value.placement ?? DEFAULT_PLACEMENT;
}
let vktooltipContentDom;
// 创建提示框dom元素,并添加到body
const createVktooltipContentDom = () => {
vktooltipContentDom = document.createElement('div');
vktooltipContentDom.id = `tooltip_${privateTooltipId}`;
if (html) {
vktooltipContentDom.innerHTML = html;
} else {
vktooltipContentDom.innerText = text;
}
document.body.appendChild(vktooltipContentDom);
};
if (behavier === 'hover') {
if (!el.getBoundingClientRect) {
// 不支持getBoundingClientRect的浏览器退化成title属性
el.setAttribute('title', text ?? html);
} else {
el.addEventListener('mouseenter', () => {
const rect = el.getBoundingClientRect();
if (!vktooltipContentDom) {
createVktooltipContentDom();
}
const scrollY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
vktooltipContentDom.style.display = 'block';
vktooltipContentDom.className = 'vktooltip-content active';
const setPosition = {
top: () => {
// 检测是否在危险区
if (rect.y < vktooltipContentDom.clientHeight) {
setPosition.bottom();
return;
}
vktooltipContentDom.classList.add('vktooltip_top');
vktooltipContentDom.style.left = `${rect.x - (vktooltipContentDom.clientWidth / 2) + (rect.width / 2)}px`;
vktooltipContentDom.style.top = `${rect.y + scrollY - vktooltipContentDom.clientHeight - 10}px`;
},
bottom: () => {
// 检测是否在危险区
const viewPortHeight = document.documentElement.clientHeight || document.body.clientHeight;
if (viewPortHeight < rect.y + rect.height + vktooltipContentDom.clientHeight) {
setPosition.top();
return;
}
vktooltipContentDom.classList.add('vktooltip_bottom');
vktooltipContentDom.style.left = `${rect.x - (vktooltipContentDom.clientWidth / 2) + (rect.width / 2)}px`;
vktooltipContentDom.style.top = `${rect.y + scrollY + rect.height + 10}px`;
},
};
setPosition[placement]();
});
el.addEventListener('mouseout', () => {
vktooltipContentDom.classList.remove('active');
vktooltipContentDom.classList.add('fade-out');
});
}
}
tooltipId += 1;
},
unbind(el) {
if (!el.getBoundingClientRect) {
return;
}
const priviteTooltipId = el.getAttribute('data-tooltip-id');
const tooltip = document.getElementById(`tooltip_${priviteTooltipId}`);
if (tooltip) {
document.body.removeChild(tooltip);
}
},
});
.vktooltip-content {
position: absolute;
max-width: 240px;
padding: 6px 10px;
line-height: 1.7;
border-radius: 5px;
font-size: 12px;
background: #333;
color: #fff;
box-shadow: 0 5px 10px -5px rgba(0, 0, 0, 0.35);
z-index: 1000;
user-select: none;
pointer-events: none;
text-transform: none;
&::before {
content: ' ';
border: 5px solid transparent;
z-index: 1001;
position: absolute;
left: 50%;
}
&.vktooltip_bottom {
&::before {
top: 0;
transform: translate(-50%, -100%);
border-top: transparent;
border-bottom-color: #333;
}
}
&.vktooltip_top {
&::before {
bottom: 0;
transform: translate(-50%, 100%);
border-bottom: transparent;
border-top-color: #333;
}
}
}
/* KEYFRAMES */
@keyframes tooltips-vert {
0% {
opacity: .9;
transform: translate(50%, 0);
}
100% {
opacity: .9;
transform: translate(0, 0);
}
}
@keyframes tooltips-horz {
0% {
opacity: 0;
transform: translate(0, 10px);
}
100% {
opacity: .9;
transform: translate(0, 0);
}
}
@keyframes tooltips-horz-reverse {
0% {
opacity: .9;
transform: translate(0, 0);
}
100% {
opacity: 0;
transform: translate(0, 10px);
}
}
.vktooltip-content.active {
display: block;
}
// .vktooltip-content.vktooltip_left,
// .vktooltip-content.vktooltip_right {
// &.active {
// animation: tooltips-vert 200ms ease-out forwards;
// }
// }
.vktooltip-content.vktooltip_top,
.vktooltip-content.vktooltip_bottom {
&.fade-out {
animation: tooltips-horz-reverse 200ms ease-in forwards;
display: none;
}
&.active {
animation: tooltips-horz 200ms ease-out forwards;
}
}