JSFiddle - React, Tailwind, and code Playground
by Imri Paloja
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
<div class="container">
<div id="editor-content" contenteditable="true" spellcheck="false">
<p>
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
</p>
<p>
<a href="https://vagrant-libvirt.github.io/vagrant-libvirt/examples.html#synced-folders" target="_blank" rel="noopener noreferrer" download="" hreflang="" ping="" type="text/html">https://vagrant-libvirt.github.io/vagrant-libvirt/examples.html#synced-folders</a>
</p>
<p class="attributes">Hover me</p>
<p class="attributes">Hover me too</p>
<p>
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
</p>
<p>
It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</p>
<p>
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
</div>
</div>
CSS
html, body {
color: #F9F9F9 !important;
background: #202020;
}
input, button, textarea {
color: inherit;
}
.container {
margin-top: 5% !important;
}
.attribute-hover-btn {
position: absolute;
padding: 4px 8px;
font-size: 12px;
background: #222;
color: #fff;
border-radius: 4px;
cursor: pointer;
z-index: 9999;
pointer-events: auto;
white-space: nowrap;
transform: translateY(-100%);
}
.attribute-hover-btn.hidden {
display: none;
}
.attribute-hover-btn {
transition: opacity 0.15s ease;
opacity: 1;
}
.attribute-hover-btn.hidden {
opacity: 0;
pointer-events: none;
}
JavaScript
const hoverBtn = document.createElement("div");
hoverBtn.className = "attribute-hover-btn hidden";
hoverBtn.textContent = "Edit";
document.body.appendChild(hoverBtn);
let currentTarget = null;
document.addEventListener("mouseover", e => {
const target = e.target.closest(".attributes");
if (!target) return;
currentTarget = target;
const rect = target.getBoundingClientRect();
hoverBtn.style.left = rect.left + window.scrollX + "px";
hoverBtn.style.top = rect.top + window.scrollY - 6 + "px";
hoverBtn.classList.remove("hidden");
});
document.addEventListener("mouseout", e => {
const to = e.relatedTarget;
// If moving into the hover button, do NOT hide
if (to && (to.closest(".attributes") || to === hoverBtn || hoverBtn.contains(to))) {
return;
}
hoverBtn.classList.add("hidden");
currentTarget = null;
});
hoverBtn.addEventListener("click", () => {
if (!currentTarget) return;
console.log("Clicked element:", currentTarget);
// your action here
});