TLDR: moving the cursor from tooltip back to triggering element closes, shows and closes the tooltip (flickers).
I need to make the tooltips open on hover and make their content clickable. I have found a working example here on SO.
As you hover over the element it shows you a tooltip which can be interacted with, once you move the cursor away from the tooltip it closes.
There is a problem though.
If you leave the tooltip and move the cursor back on the element which triggered the tooltip, the tooltip pops back up, but dissapears after a moment ("flickering"). You need to move the cursor away from the element and back on the element for the tooltip to show again.
What I am trying to do, is check if the cursor is back on the triggering element and if that is the case not run the closing function (tooltip.hide()
).
I have tried to do this by imitating the existing process from the example found on SO. That is, check if the tooltip has lost :hover
, setTimout
(300ms) and check if cursor is now positioned on the triggering element or back on the tooltip.
Here is a jsFiddle example.
This is the code. The problematic code is between the two looong comment lines.
Note: Moving the cursor away from the triggering element and back on the triggering element also triggers the flickering.
//https://stackoverflow.com/questions/67993080/bootstrap-5-make-tooltip-hoverable-and-link-clickable
var tooltipTriggerList = [].slice.call(document.querySelectorAll('button'))
for (let tt of tooltipTriggerList){
tt.setAttribute("data-bs-placement","top")
}
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
const tooltip = new bootstrap.Tooltip(tooltipTriggerEl, {
trigger: "manual",
'customClass': 'custom-tooltip'
})
let tooltipElTimeout;
let currentToolTip;
let currentTooltipTimeout;
tooltipTriggerEl.addEventListener("mouseenter", function () {
let toolTipID;
// Clear Set Timeout
clearTimeout(currentTooltipTimeout);
// Show Tooltip
tooltip.show();
// Assign current tooltip ID to toolTipID variable
toolTipID = tooltipTriggerEl.getAttribute("aria-describedby");
// Assign current tooltip to currentToolTip variable
currentToolTip = document.querySelector(`#${toolTipID}`);
/*******************************************************************/
// Hide tooltip on tooltip mouse leave
currentToolTip.addEventListener("mouseleave", function () {
currentTooltipTimeout = setTimeout(()=>{
console.log("!currentToolTip.matches(':hover')");
console.log(!currentToolTip.matches(":hover"));
if(!tooltipTriggerEl.matches(":hover")){
console.log("!tooltipTriggerEl.matches(':hover')");
console.log(!tooltipTriggerEl.matches(":hover"));
if (!currentToolTip.matches(":hover")) {
tooltip.hide();
}
}
}, 300)
});
/***********************************************************************/
});
tooltipTriggerEl.addEventListener("mouseleave", function () {
// SetTimeout before tooltip disappears
tooltipTimeout = setTimeout(function () {
// Hide tooltip if not hovered.
if (!currentToolTip.matches(":hover")) {
tooltip.hide();
}
}, 100);
});
return tooltip;
})
Thank you
Edit:
Amine Ramouls answer is correct. isHidden
also needs to bet set to false on the 2cnd eventListener, otherwise the tooltips no longer work (problem with aria-describedby
).
In your code, you have an event listener that adds another event listener, which is a big mistake because it adds an infinite number of event listeners to your element.
To fix this, organize your code like this:
//https://stackoverflow.com/questions/67993080/bootstrap-5-make-tooltip-hoverable-and-link-clickable
var tooltipTriggerList = [].slice.call(document.querySelectorAll('button'))
for (let tt of tooltipTriggerList){
tt.setAttribute("data-bs-placement","top")
}
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
const tooltip = new bootstrap.Tooltip(tooltipTriggerEl, {
trigger: "manual",
'customClass': 'custom-tooltip'
})
let isHidden = true;
let currentTooltipTimeout;
tooltipTriggerEl.addEventListener("mouseenter", function () {
let toolTipID;
// Clear Set Timeout
clearTimeout(tooltipElTimeout);
clearTimeout(currentTooltipTimeout);
if (isHidden)
{
tooltip.show();
isHidden=false;
}
});
// Hide tooltip on tooltip mouse leave
tooltipTriggerEl.addEventListener("mouseleave", function () {
console.log("!currentToolTip.matches(':hover')");
if(!tooltipTriggerEl.matches(":hover")){
currentTooltipTimeout=setTimeout(()=>{
if (!isHidden && !tooltipTriggerEl.matches(":hover")){
tooltip.hide();
isHidden=true;
}
console.log("!tooltipTriggerEl.matches(':hover')");
console.log(!tooltipTriggerEl.matches(":hover"));
}, 3000)
}
});
return tooltip;
})
I've just added the isHidden variable to check if the popup info is hidden or not. You can do this with the element if you can get it by a query selector request. That's it! Enjoy your life.
Edit: I forgot to mention that I've added a 3-second delay before checking if the popup is hidden or not.