JSFiddle - React, Tailwind, and code Playground
HTML
<span title="This is a tooltip to the right" data-tooltip="right">Hover over me for a tooltip to the right</span><br>
CSS
span{
background:#000;
padding:10px;
color:#fff;
}
.tooltip {
position: absolute;
background:#f0f; /*Change this color to see the triangle change color too */
color:#fff;
padding: 5px;
z-index: 1000;
max-width: 200px;
transition: opacity .2s ease;
opacity: 0;
box-sizing: border-box;
border-radius: 5px;
pointer-events: none;
}
.tooltip:after {
content: "";
position: absolute;
width: 10px;
height: 10px;
background: inherit;
opacity: 0;
transition: opacity .2s ease;
z-index: -1;
}
.tooltip.fade {
opacity: 1;
}
.tooltip.fade:after {
opacity: 1;
}
.tooltip.right:after {
top: 50%;
left: 0;
transform: translate(-50%, -50%) rotate(45deg);
}
JavaScript
const parent = document.querySelector("span");
const tip = document.createElement("div");
tip.textContent = parent.title;
tip.classList.add("tooltip");
parent.addEventListener("mouseenter", event => {
/*Add the tooltip to the DOM, and then place it where it needs to go*/
addTooltip(parent, tip);
positionTip(parent, tip);
});
parent.addEventListener("mouseleave", event => {
//remove it when it's no longer needed
removeTooltip(parent, tip);
});
function addTooltip(parent, tip) {
/*prevent the default tooltip behavior from showing by temporarily emptying the title attribute*/
parent.dataset.title = parent.title;
parent.title = "";
/*getting rid of the previous timeouts before adding a new one prevents strange flickering and
incorrect states*/
if (parent.toOut !== null) clearTimeout(parent.toOut);
/*after 500 ms has passed, we will fade the tooltip in*/
parent.appendChild(tip);
parent.toIn = setTimeout(() => {
tip.classList.add("fade");
parent.toIn = null;
}, 500);
}
function removeTooltip(parent, tip) {
/*restore the original title attribute data*/
parent.title = parent.dataset.title;
/*getting rid of the previous timeouts before adding a new one prevents strange flickering and
incorrect states*/
if (parent.toIn !== null) clearTimeout(parent.toIn);
/*fade the tooltip out and remove it after the transition is complete*/
tip.classList.remove("fade");
parent.toOut = setTimeout(() => {
parent.removeChild(tip);
parent.toOut = null;
}, 500);
}
function positionTip(parent, tip) {
const orientation = parent.dataset.tooltip;
const padding = 10;
/*use CSS styles to position the tooltip, dynamically determined by the height and width of the tooltip
and the item it's attached to*/
tip.style.left = `${parent.offsetLeft +
parent.offsetWidth +
padding}px`;
tip.style.top = `${parent.offsetTop +
parent.offsetHeight / 2 -
tip.offsetHeight / 2}px`;
tip.classList.add("right");
}