Tooltip Directive
The v-tooltip
directive is used to display a tooltip when the user hovers over an element.
Usage
Both light and dark variants are available for the tooltip. toggle the dark mode to see the difference.
Video preview of the tooltip directive because of css issues in the docs.
View Code
vue
<script setup>
const tooltipText = "This is a tooltip";
const toggleDark = () => {
document.documentElement.classList.toggle("dark");
};
</script>
<template>
<main
class="w-full rounded-2xl p-5 bg-gray-50 dark:bg-zinc-900 dark:text-white flex flex-col space-y-10 justify-center items-center"
>
<button @click="toggleDark">Dark</button>
<div v-tooltip="tooltipText">Hover me for top tooltip</div>
<div v-tooltip.right="tooltipText">Hover me for right tooltip</div>
<div v-tooltip.left="tooltipText">Hover me for left tooltip</div>
<div v-tooltip.bottom="tooltipText">Hover me for bottom tooltip</div>
<div v-tooltip="tooltipText">Hover me for a tooltip (default top)</div>
</main>
</template>
<style>
:root {
--tooltip-arrow-size: 5px;
--tooltip-arrow-bg: #f5f5f5;
--tooltip-border: #e4e4e7;
}
/* dark */
.dark {
--tooltip-arrow-bg: #27272a;
--tooltip-border: #3d3d40;
}
.tooltip-top::after {
content: " ";
position: absolute;
background-color: var(--tooltip-arrow-bg);
border-right: 1px solid var(--tooltip-border);
border-bottom: 1px solid var(--tooltip-border);
width: 12px;
height: 12px;
left: 50%;
margin-left: -5px;
top: 100%;
transform: rotate(45deg);
translate: 0 -46%;
}
.tooltip-bottom::after {
content: " ";
position: absolute;
background-color: var(--tooltip-arrow-bg);
border-top: 1px solid var(--tooltip-border);
border-left: 1px solid var(--tooltip-border);
width: 12px;
height: 12px;
left: 50%;
margin-left: -5px;
bottom: 100%;
transform: rotate(45deg);
translate: 0 46%;
}
.tooltip-right::after {
content: " ";
position: absolute;
background-color: var(--tooltip-arrow-bg);
border-left: 1px solid var(--tooltip-border);
border-bottom: 1px solid var(--tooltip-border);
width: 12px;
height: 12px;
top: 50%;
margin-top: -5px;
right: 100%;
transform: rotate(45deg);
translate: 46% 0;
}
.tooltip-left::after {
content: " ";
position: absolute;
background-color: var(--tooltip-arrow-bg);
border-top: 1px solid var(--tooltip-border);
border-right: 1px solid var(--tooltip-border);
width: 12px;
height: 12px;
top: 50%;
margin-top: -5px;
left: 100%;
transform: rotate(45deg);
translate: -46% 0;
}
</style>
Arguments
The v-tooltip
directive does not accept any arguments.
Modifiers
The v-tooltip
directive takes the following modifiers:
Modifier | Description |
---|---|
left | Displays the tooltip to the left of the element. |
right | Displays the tooltip to the right of the element. |
top | Displays the tooltip above the element. |
bottom | Displays the tooltip below the element. |
Source Code
js
import { onBeforeUnmount } from "vue";
export const vToolTip = {
mounted(el, binding) {
// Create the tooltip element
const tooltipEl = document.createElement("div");
tooltipEl.classList.add("tooltip");
tooltipEl.style.position = "absolute";
tooltipEl.style.visibility = "hidden";
tooltipEl.style.opacity = 0;
tooltipEl.style.transition = "opacity 0.2s";
tooltipEl.style.padding = "10px";
tooltipEl.style.zIndex = 10;
tooltipEl.style.textAlign = "center"; // Center the text
// Add the arrow element
const arrowEl = document.createElement("div");
arrowEl.classList.add("tooltip-arrow");
tooltipEl.appendChild(arrowEl);
// Add the tooltip content
tooltipEl.textContent = binding.value;
// Position the tooltip
const showTooltip = () => {
tooltipEl.style.visibility = "visible";
tooltipEl.style.opacity = 1;
const elRect = el.getBoundingClientRect();
const tooltipRect = tooltipEl.getBoundingClientRect();
const arrowSize = 8;
// handle all 4 positions by modifiers top, right, bottom, left
if (binding.modifiers.top) {
tooltipEl.classList.add("tooltip-top");
tooltipEl.style.top = `${
elRect.top - tooltipRect.height - arrowSize
}px`;
tooltipEl.style.left = `${
elRect.left + elRect.width / 2 - tooltipRect.width / 2
}px`;
arrowEl.style.top = `${tooltipRect.height - 1}px`;
arrowEl.style.left = `${tooltipRect.width / 2 - arrowSize}px`;
} else if (binding.modifiers.right) {
tooltipEl.classList.add("tooltip-right");
tooltipEl.style.top = `${
elRect.top + elRect.height / 2 - tooltipRect.height / 2
}px`;
tooltipEl.style.left = `${elRect.right + arrowSize}px`;
arrowEl.style.top = `${tooltipRect.height / 2 - arrowSize}px`;
arrowEl.style.left = `-${arrowSize}px`;
} else if (binding.modifiers.bottom) {
tooltipEl.classList.add("tooltip-bottom");
tooltipEl.style.top = `${elRect.bottom + arrowSize}px`;
tooltipEl.style.left = `${
elRect.left + elRect.width / 2 - tooltipRect.width / 2
}px`;
arrowEl.style.top = `-${arrowSize}px`;
arrowEl.style.left = `${tooltipRect.width / 2 - arrowSize}px`;
} else if (binding.modifiers.left) {
tooltipEl.classList.add("tooltip-left");
tooltipEl.style.top = `${
elRect.top + elRect.height / 2 - tooltipRect.height / 2
}px`;
tooltipEl.style.left = `${
elRect.left - tooltipRect.width - arrowSize
}px`;
arrowEl.style.top = `${tooltipRect.height / 2 - arrowSize}px`;
arrowEl.style.left = `${tooltipRect.width - 1}px`;
} else {
// default to top
tooltipEl.classList.add("tooltip-top");
tooltipEl.style.top = `${
elRect.top - tooltipRect.height - arrowSize
}px`;
tooltipEl.style.left = `${
elRect.left + elRect.width / 2 - tooltipRect.width / 2
}px`;
arrowEl.style.top = `${tooltipRect.height - 1}px`;
arrowEl.style.left = `${tooltipRect.width / 2 - arrowSize}px`;
}
};
const hideTooltip = () => {
tooltipEl.style.visibility = "hidden";
tooltipEl.style.opacity = 0;
};
el.addEventListener("mouseenter", showTooltip);
el.addEventListener("mouseleave", hideTooltip);
el.addEventListener("mouseenter", showTooltip);
el.addEventListener("mouseleave", hideTooltip);
// Ensure cleanup when component is unmounted
onBeforeUnmount(() => {
el.removeEventListener("mouseenter", showTooltip);
el.removeEventListener("mouseleave", hideTooltip);
el.removeChild(tooltipEl);
});
// Attach the tooltip and arrow elements to the element
el.appendChild(tooltipEl);
},
};