Reputation: 37
I have a nested structure of div elements where the innermost child contains a tooltip. The parent container (parent class) has the CSS property overflow: hidden
. However, when hovering over the tooltip, it gets clipped and doesn't overflow outside the boundaries of the parent. Here's a simplified version of the structure:
document.querySelectorAll(".tooltip_container").forEach((tooltip) => {
const tooltipText = tooltip.getAttribute("data-tooltip");
const tooltipContent = document.createElement("div");
tooltipContent.className = "tooltip_content";
tooltipContent.innerText = tooltipText;
tooltip.appendChild(tooltipContent);
});
.parent_class {
position: relative;
width: 300px;
height: 100px;
margin: 50px;
overflow: auto;
border: 2px solid #ccc;
padding: 20px;
background-color: #f9f9f9;
}
.child_level_four {
position: relative;
margin-top: 0px;
display: flex;
flex-direction: column;
gap: 16px;
}
.tooltip_container {
position: relative;
display: inline-block;
}
.tooltip_content {
position: absolute;
bottom: 100%;
left: -15%;
transform: translateX(-50%);
background-color: #333;
color: #fff;
padding: 8px;
border-radius: 4px;
opacity: 0;
visibility: hidden;
white-space: nowrap;
transition: all 0.3s ease-in-out;
z-index: 1000;
}
.tooltip_content::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 5px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}
.tooltip_container:hover .tooltip_content {
opacity: 1;
visibility: visible;
transform: translateY(-18%);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pure JS Tooltip</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="parent_class">
<div class="child_level_one">
<div class="child_level_two">
<div class="child_level_three">
<div class="child_level_four">
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Upvotes: 1
Views: 93
Reputation: 1
This happen because the overflow: hidden
attribute on .parent_class
truncates any information that exceeds its borders, including your tooltip. Here is the simple way to make the tooltip visible:
overflow: hidden
issue, replace below with your .tooltip_content
class.Solution:
.tooltip_content {
/* position: absolute;
bottom: 100%;
left: -50%;
transform: translateX(-50%);*/
position: fixed; /* Position relative to viewport */
bottom: auto; /* Adjust position for viewport */
left: auto;
transform: none; /* Remove or adjust transform */
background-color: #333;
color: #fff;
padding: 8px;
border-radius: 4px;
opacity: 0;
visibility: hidden;
white-space: nowrap;
transition: all 0.3s ease-in-out;
z-index: 1000;
}
getBoundingClientRect()
, and then moves it with position: fixed
. This will position the tooltip based on the viewport, not constrained within the boundaries of its parent container.
Also no overflow clipping, because we are using position: fixed
, the tooltip isn't affected by overflow: hidden
on the parent container, and it can pop out of it.Solution:
document.querySelectorAll(".tooltip_container").forEach((tooltip) => {
const tooltipText = tooltip.getAttribute("data-tooltip");
// Create the tooltip element
const tooltipContent = document.createElement("div");
tooltipContent.className = "tooltip_content";
tooltipContent.innerText = tooltipText;
tooltip.appendChild(tooltipContent);
// When hovering over the button, position the tooltip
tooltip.addEventListener("mouseenter", () => {
const rect = tooltip.getBoundingClientRect(); // Get position of the button
const tooltipRect = tooltipContent.getBoundingClientRect(); // Get size of the tooltip
// Calculate the position of the tooltip based on the button's position
tooltipContent.style.position = "fixed";
tooltipContent.style.top = `${rect.top - tooltipRect.height - 0}px`; // Position above the button
tooltipContent.style.left = `${rect.left + rect.width / 2 - tooltipRect.width / 2}px`; // Center it above the button
// Show the tooltip
tooltipContent.style.opacity = 1;
tooltipContent.style.visibility = "visible";
});
// Hide tooltip when mouse leaves
tooltip.addEventListener("mouseleave", () => {
tooltipContent.style.opacity = 0;
tooltipContent.style.visibility = "hidden";
});
});
.parent_class {
position: relative;
width: 300px;
height: 100px;
margin: 50px;
overflow: hidden; /* Prevents overflow of the parent */
border: 2px solid #ccc;
padding: 20px;
background-color: #f9f9f9;
}
.tooltip_container {
position: relative; /* Tooltip stays aligned to the button */
display: inline-block;
}
.tooltip_content {
position: absolute;
bottom: 100%;
left: 50%;
top: 37px;
transform: translateX(-50%);
background-color: #333;
color: #fff;
padding: 8px;
padding-bottom: 27px;
border-radius: 4px;
opacity: 0;
visibility: hidden;
white-space: nowrap;
transition: all 0.3s ease-in-out;
z-index: 1000;
}
.tooltip_content::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 5px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}
.tooltip_container:hover .tooltip_content {
opacity: 1;
visibility: visible;
transform: translateY(-18%);
}
<div class="parent_class">
<div class="child_level_one">
<div class="child_level_two">
<div class="child_level_three">
<div class="child_level_four">
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
</div>
</div>
</div>
</div>
</div>
Upvotes: 0
Reputation: 178350
That is not trivial. We need to append to body and make a sort of mouse follow:
document.querySelectorAll(".tooltip_container").forEach((tooltip) => {
const tooltipText = tooltip.getAttribute("data-tooltip");
const tooltipContent = document.createElement("div");
tooltipContent.className = "tooltip_content";
tooltipContent.innerText = tooltipText;
// Append tooltip to the body
document.body.appendChild(tooltipContent);
tooltip.addEventListener("mouseenter", (event) => {
tooltipContent.style.visibility = "visible";
tooltipContent.style.opacity = "1";
// Position tooltip initially
updateTooltipPosition(event);
});
tooltip.addEventListener("mouseleave", () => {
tooltipContent.style.visibility = "hidden";
tooltipContent.style.opacity = "0";
});
tooltip.addEventListener("mousemove", (event) => {
updateTooltipPosition(event);
});
function updateTooltipPosition(event) {
const offset = 10; // Offset to avoid flickering - since mousing over the tooltip triggers mouseleave
const tooltipWidth = tooltipContent.offsetWidth;
const tooltipHeight = tooltipContent.offsetHeight;
let top = event.clientY - tooltipHeight - offset;
let left = event.clientX - tooltipWidth / 2;
// Prevent tooltip from going out of the viewport
if (top < 0) top = event.clientY + offset; // Position below if it goes above
if (left < 0) left = offset; // Align to the left edge
if (left + tooltipWidth > window.innerWidth)
left = window.innerWidth - tooltipWidth - offset; // Align to the right edge
tooltipContent.style.top = `${top}px`;
tooltipContent.style.left = `${left}px`;
}
});
.parent_class {
position: relative;
width: 300px;
height: 100px;
margin: 50px;
overflow: auto;
border: 2px solid #ccc;
padding: 20px;
background-color: #f9f9f9;
}
.child_level_four {
position: relative;
margin-top: 0px;
display: flex;
flex-direction: column;
gap: 16px;
}
.tooltip_container {
position: relative;
display: inline-block;
z-index: 1000;
}
.tooltip_content {
position: absolute;
background-color: #333;
color: #fff;
padding: 8px;
border-radius: 4px;
opacity: 0;
visibility: hidden;
white-space: nowrap;
transition: opacity 0.3s ease-in-out;
z-index: 9999;
}
.tooltip_content::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 5px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}
.tooltip_container:hover .tooltip_content {
opacity: 1;
visibility: visible;
transform: translateY(-18%);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pure JS Tooltip</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="parent_class">
<div class="child_level_one">
<div class="child_level_two">
<div class="child_level_three">
<div class="child_level_four">
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
<div class="tooltip_container" data-tooltip="Overflow Tooltip Text">
<button>Hover Me</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Upvotes: 0