Reputation: 789
I am trying to make a tooltip in plain JavaScript which is shown on hover
. Like the one in Stack Overflow on hover over the profile name a div
is shown.
I tried using onmouseover
, onmouseout
and added setTimeout
to give the user a few seconds to move mouse over the tooltip content. But it was not working as I thought.
I really like pure JavaScript more than using any libraries. Can some one help me out?
This is what I did in pure JavaScript.
<div class = "name" onmouseover="show()" onmouseout="hide()">
NAME
<div class = "tooltip">
PROFILE DETAILS
</div>
</div>
<div class = "name" onmouseover="show()" onmouseout="hide()">
NAME 2
<div class = "tooltip" >
PROFILE DETAILS 2
</div>
</div>
<div class = "name" onmouseover="show()" onmouseout="hide()">
NAME 3
<div class = "tooltip" >
PROFILE DETAILS 3
</div>
</div>
.name{
float:left;
margin:100px;
border:1px solid black;
}
.tooltip{
position:absolute;
margin:5px;
width:200px;
height:100px;
border:1px solid black;
display:none;
}
var name = document.getElementsByclassName("name");
var tp = document.getElementsByclassName("tooltip");
function show(){
tp.style.display="block";
}
function hide(){
tp.style.display="";
}
Upvotes: 37
Views: 118924
Reputation: 898
Even for $(document).ready
, it’s hard to accomplish in pure JS—see here.
So I’m using a simple version:
window.addEventListener('load', () => {
const couponcodes = document.getElementsByClassName('couponcode');
for (let i = 0; i < couponcodes.length; i++) {
const base = couponcodes[i]
const tip = base.getElementsByClassName('coupontooltip')[0]
if (base == null) continue
if (tip == null) continue
base.addEventListener('mouseover', function() {
tip.removeAttribute('style')
})
base.addEventListener('mouseout', function() {
tip.style.display = 'none'
})
}
})
.coupontooltip {
background: #C8C8C8;
margin-left: 28px;
padding: 10px;
position: absolute;
z-index: 1000;
width: 200px;
height: 100px;
}
.couponcode {
margin: 100px;
}
<div class="couponcode">
First Link
<span class="coupontooltip" style="display: none;">Content 1</span>
</div>
<div class="couponcode">
Second Link
<span class="coupontooltip" style="display: none;"> Content 2</span>
</div>
Upvotes: 3
Reputation: 8895
The question said:
Plain JavaScript tooltip
I think, in modern times, we can use vanilla JS and CSS to implement anything and it is more easy that using libraries. Especially, when we want to provide a good looking style and have custom control of it.
This example is a simple implementation of pure JS and CSS.
Let's create a tooltip using CSS
and add modify the behavior using JS
. For this example, we will define a tip
attribute to storage the text that will be shown on the tooltip.
visibility: hidden;
and opacity: 0;
will maintain the .tooltip
hidden and will appear when [tip]:hover
. Also, we can reposition the point of emission from the tooltip will be shown using style.transform
.
const elements = [...document.querySelectorAll('[tip]')]
for (const el of elements) {
const tip = document.createElement('div')
tip.classList.add('tooltip')
tip.textContent = el.getAttribute('tip')
const x = el.hasAttribute('tip-left') ? 'calc(-100% - 5px)' : '16px'
const y = el.hasAttribute('tip-top') ? '-100%' : '0'
tip.style.transform = `translate(${x}, ${y})`
el.appendChild(tip)
el.onpointermove = e => {
if (e.target !== e.currentTarget) return
const rect = tip.getBoundingClientRect()
const rectWidth = rect.width + 16
const vWidth = window.innerWidth - rectWidth
const rectX = el.hasAttribute('tip-left') ? e.clientX - rectWidth : e.clientX + rectWidth
const minX = el.hasAttribute('tip-left') ? 0 : rectX
const maxX = el.hasAttribute('tip-left') ? vWidth : window.innerWidth
const x = rectX < minX ? rectWidth : rectX > maxX ? vWidth : e.clientX
tip.style.left = `${x}px`
tip.style.top = `${e.clientY}px`
}
}
[tip] .tooltip {
position: fixed;
font-size: 16px;
line-height: 20px;
padding: 5px;
background: #444;
border: 1px solid #222;
visibility: hidden;
opacity: 0;
box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.2);
transition: opacity 0.3s, visibility 0s;
color: white;
min-width: 120px;
}
[tip]:hover .tooltip {
visibility: visible;
opacity: 1;
}
button {
display: block;
cursor: pointer;
padding: 8px 15px;
border: 1px solid gray;
border-radius: 4px;
margin: 50px 5px;
width: 200px;
font-size: 18px;
background: white;
}
button:hover {
border-color: dodgerblue;
}
<button tip="Click me here!">I have a tooltip</button>
<button tip="Click me here too!" tip-top tip-left>Top-left tooltip</button>
Upvotes: 14
Reputation: 130
Ran into some lag with popper+tippy, started searching alternatives and found @Teocci's great answer above!
I needed the following tweaks, so this is a modified code for:
function initTips() {
// purge previous for dynamic render
Array.from(document.querySelectorAll('.tooltip')).forEach(el => {
el.remove()
})
// built upon: https://stackoverflow.com/a/69340293/10885535
Array.from(document.querySelectorAll('[data-tip]')).forEach(el => {
// tip
let tip = document.createElement('div')
tip.classList.add('tooltip')
tip.innerText = el.getAttribute('data-tip')
document.body.appendChild(tip)
// arrow
let arrow = document.createElement('div')
arrow.classList.add('tooltip-arrow')
tip.appendChild(arrow)
// position tip + arrow once added
setTimeout(() => {
let elmPos = el.getBoundingClientRect()
let tipPos = tip.getBoundingClientRect()
tip.style.left = (elmPos.left + (elmPos.width - tipPos.width) / 2) + 'px'
tip.style.top = (elmPos.bottom + 5) + 'px'
arrow.style.left = (tipPos.width / 2 - 5) + 'px'
}, 0)
// toggle with mouse
el.onmouseover = e => {
tip.style.opacity = 1
tip.style.visibility = 'visible'
e.stopPropagation() // stop parent
};
el.onmouseout = e => {
tip.style.opacity = 0
tip.style.visibility = 'hidden'
};
});
}
// kickoff
initTips()
// test calling again ie after dynamic content
setTimeout(initTips, 1000)
.tooltip {
position: fixed;
z-index: 99;
font-size: 12px;
line-height: 12px;
padding: 5px;
background: #222;
color: #fff;
border: 1px solid #aaa;
border-radius: 5px;
font-family: sans-serif;
box-sizing: border-box;
/*box-shadow: -1px 2px 5px rgba(0, 0, 0, 0.2);*/
transition: opacity 0.3s, visibility 0s;
visibility: hidden;
opacity: 0;
}
.tooltip-arrow {
position: absolute;
top: -4px;
width: 7px;
height: 7px;
background: inherit;
transform: rotate(45deg);
border-top: 1px solid #aaa;
border-left: 1px solid #aaa;
}
/* just for demo */
.box {
margin: 25px;
padding: 25px;
}
<div class="box" style="background:orange;" data-tip="hello i'm the parent">
<div class="box" style="background:green;" data-tip="hi i'm the child"></div>
</div>
Upvotes: 3
Reputation: 207511
This uses CSS pseudo hover to set the display of the hidden element. The display none needs to be in the style and not on the element so it can be overwritten in the hover.
.couponcode:hover .coupontooltip {
/* NEW */
display: block;
}
.coupontooltip {
display: none;
/* NEW */
background: #C8C8C8;
margin-left: 28px;
padding: 10px;
position: absolute;
z-index: 1000;
width: 200px;
height: 100px;
}
.couponcode {
margin: 100px;
}
<div class="couponcode">First Link
<span class="coupontooltip">Content 1</span>
<!-- UPDATED -->
</div>
<div class="couponcode">Second Link
<span class="coupontooltip"> Content 2</span>
<!-- UPDATED -->
</div>
Follow-Up:
If you need to support really old browsers, you would need to add a class to the outside element when the mouse enters the div. And remove that class when mouse leaves.
Your code did not work because what is tp? Is a collection of elements and you are treating it as one. What you would need to do is pass in the reference to the element
HTML:
<div class = "name" onmouseover="show(this)" onmouseout="hide(this)"> <!-- added "this" 2 times -->
**JavaScript:
//var name = document.getElementsByclassName("name"); /* not needed */
// var tp = document.getElementsByclassName("tooltip"); /* not needed */
function show (elem) { /* added argument */
elem.style.display="block"; /* changed variable to argument */
}
function hide (elem) { /* added argument */
elem.style.display=""; /* changed variable to argument */
}
Upvotes: 49
Reputation: 71
I was looking for something like this, and I came across this page. It helped me, but I had to fix your code, for it to work. I think that this is what you tried. You have to reference your objects by their "ID". Here is what I've done, and it works:
function show(elem) {
elem.style.display = "block";
}
function hide(elem) {
elem.style.display = "";
}
.name {
float: left;
margin: 100px;
border: 1px solid black;
}
.tooltip {
position: absolute;
margin: 5px;
width: 200px;
height: 50px;
border: 1px solid black;
display: none;
}
<div class="name" onmouseover="show(tooltip1)" onmouseout="hide(tooltip1)">
NAME
<div class="tooltip" id="tooltip1">
PROFILE DETAILS
</div>
</div>
<div class="name" onmouseover="show(tooltip2)" onmouseout="hide(tooltip2)">
NAME 2
<div class="tooltip" id="tooltip2">
PROFILE DETAILS 2
</div>
</div>
<div class="name" onmouseover="show(tooltip3)" onmouseout="hide(tooltip3)">
NAME 3
<div class="tooltip" id="tooltip3">
PROFILE DETAILS 3
</div>
</div>
Upvotes: 7
Reputation: 71
For non-customized tooltip, you can just add the message you want to display in tooltip in the title attribute of the main div
. Just like this:
<div class = "name" onmouseover="show()" onmouseout="hide()" title="PROFILE DETAILS">
Then there is no need to add the onmouseover
and onmouseout
event handlers.
Upvotes: 7