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
});