2025-04-09 15:20:57 -05:00
|
|
|
// custom popups that appear on hover that uses the alt/aria-label attribute on elements
|
|
|
|
|
// replacement for using title without the issue it causes with screen readers and such
|
2025-04-09 13:51:07 -05:00
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
|
let popup = document.getElementById("alt-popup");
|
|
|
|
|
|
|
|
|
|
// add popup to all elements with alt or aria-label
|
|
|
|
|
function initializePopups() {
|
|
|
|
|
// find all elements with alt or aria-label
|
|
|
|
|
const elementsWithAlt = document.querySelectorAll("[alt], [aria-label]");
|
|
|
|
|
|
|
|
|
|
// add mouse events to each element
|
|
|
|
|
elementsWithAlt.forEach((element) => {
|
|
|
|
|
element.addEventListener("mousemove", showPopup);
|
|
|
|
|
element.addEventListener("mouseout", hidePopup);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// show popup and position it near the cursor
|
|
|
|
|
function showPopup(event) {
|
|
|
|
|
// get alt text or aria-label
|
|
|
|
|
const altText = event.target.getAttribute("alt");
|
|
|
|
|
const ariaLabel = event.target.getAttribute("aria-label");
|
|
|
|
|
|
|
|
|
|
// Use alt text if available, otherwise use aria-label
|
|
|
|
|
const displayText = altText || ariaLabel;
|
|
|
|
|
|
|
|
|
|
if (displayText) {
|
|
|
|
|
// use text in popup
|
|
|
|
|
popup.textContent = displayText;
|
|
|
|
|
|
|
|
|
|
// Make the popup visible but with position:fixed and visibility:hidden
|
|
|
|
|
// so we can measure its dimensions without it being visible yet
|
|
|
|
|
popup.style.opacity = "0";
|
|
|
|
|
popup.style.visibility = "visible";
|
|
|
|
|
popup.style.position = "fixed"; // Changed from absolute to fixed
|
|
|
|
|
|
|
|
|
|
// Get viewport dimensions
|
|
|
|
|
const viewportWidth = window.innerWidth;
|
|
|
|
|
const viewportHeight = window.innerHeight;
|
|
|
|
|
|
|
|
|
|
// Get popup dimensions
|
|
|
|
|
const popupWidth = popup.offsetWidth;
|
|
|
|
|
const popupHeight = popup.offsetHeight;
|
|
|
|
|
|
|
|
|
|
// Use clientX/clientY which are relative to the viewport (for fixed positioning)
|
|
|
|
|
let leftPos = event.clientX + 20;
|
|
|
|
|
let topPos = event.clientY + 20;
|
|
|
|
|
|
|
|
|
|
// Check if popup would extend beyond right edge of viewport
|
|
|
|
|
if (leftPos + popupWidth > viewportWidth) {
|
|
|
|
|
// Flip to left side of cursor
|
|
|
|
|
leftPos = event.clientX - popupWidth - 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if popup would extend beyond bottom edge of viewport
|
|
|
|
|
if (topPos + popupHeight > viewportHeight) {
|
|
|
|
|
// Flip to above cursor
|
|
|
|
|
topPos = event.clientY - popupHeight - 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Additional check if flipping to left puts it offscreen
|
|
|
|
|
if (leftPos < 0) {
|
|
|
|
|
leftPos = 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Additional check if flipping to top puts it offscreen
|
|
|
|
|
if (topPos < 0) {
|
|
|
|
|
topPos = 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the final position and make it visible
|
|
|
|
|
popup.style.left = leftPos + "px";
|
|
|
|
|
popup.style.top = topPos + "px";
|
|
|
|
|
popup.style.opacity = "1";
|
|
|
|
|
popup.style.visibility = "visible";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hidePopup() {
|
|
|
|
|
popup.style.opacity = "0";
|
|
|
|
|
popup.style.visibility = "hidden";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
initializePopups();
|
|
|
|
|
|
|
|
|
|
if (window.MutationObserver) {
|
|
|
|
|
const observer = new MutationObserver((mutations) => {
|
|
|
|
|
mutations.forEach((mutation) => {
|
|
|
|
|
if (mutation.addedNodes.length) {
|
|
|
|
|
initializePopups();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Only set up the observer once
|
|
|
|
|
if (document.body) {
|
|
|
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|