Tooltip
Tooltip
Overview
A tooltip provides extra information about a form field, a link, a button, or other focusable element. It must be triggered by both focus and hover events and remains on the screen as long as the trigger has the focus. The focus does not move to the tooltip.
Turn on a screen reader to experience this example in action.
Attribute/Option | Description |
---|---|
styleClass | This attribute/option specifies the CSS class to be used for customized styling of the tooltip. If this option is not specified, a default tooltip styling is used. |
showOnFocus | This attribute/option (boolean value, true or false) specifies if the tooltip should be displayed if the element has a focus. By default, this option is enabled. |
keepTooltipOnMouseOver | This attribute/option (boolean value, true or false) specifies if the tooltip should be displayed when an user moves the mouse over the tooltip. By default, this option is enabled. |
HTML Source Code
<table class="data">
<tr>
<th width="150px" >Attribute/Option</th>
<th>Description</th>
</tr>
<tr>
<td>styleClass</td>
<td>This attribute/option specifies the CSS class to be used for customized styling of the tooltip.
If this option is not specified, a default tooltip styling is used.
</td>
</tr>
<tr>
<td>showOnFocus</td>
<td>This attribute/option (boolean value, true or false) specifies if the tooltip should be displayed if the element has a focus.
By default, this option is enabled.
</td>
</tr>
<tr>
<td>keepTooltipOnMouseOver</td>
<td>This attribute/option (boolean value, true or false) specifies if the tooltip should be displayed when an user moves the mouse over the tooltip.
By default, this option is enabled.
</td>
</tr>
</table>
<br/>
<p>
<label for="nameInput">First Name</label>
<input id="nameInput" type="text" autocomplete="given-name" data-tooltip="Your given name"
class="deque-input">
</p>
<p>
<label for="lastInput">Last Name</label>
<input id="lastInput" type="text"
autocomplete="family-name"
data-tooltip="Your surname or family name. This tooltip has a custom styling." class="deque-input">
</p>
<p>
<a id="demo-link1" href="javascript:void(0);"
data-tooltip="Tooltips can contain markup such as
<strong>&lt;strong&gt;</strong> and <em>&lt;em&gt;</em>
and even images:<br> <img src='https://dequeuniversity.com/assets/images/module-aria/sun.png'
alt='Emoji smiling sun' height='24' width='24'>">
Link 1
</a>
</p>
<p>
<a id="demo-link2" href="javascript:void(0);"
data-tooltip="This is non-functional demo
link with a tooltip. This tooltip has a custom styling.">
Link 2
</a>
</p>
<p>
<button id="demo-button1" class="deque-button"
data-tooltip="This particular tooltip is longer than the others,
and spans several rows. The height is calculated automatically by the
script and the offset is applied appropriately. It is possible to create
long tooltips, but it is NOT recommended. The aria-describedby attribute
does not allow screen reader users to pause in the middle of a tooltip
and start again where they left off. They have to focus again on
the element and listen to the whole tooltip from the beginning.">
Button 1
</button>
<button id="demo-button2" class="deque-button"
data-tooltip="This is non-functional demo link with a tooltip. This tooltip has a custom styling.">
Button 2
</button>
</p>
<p>
<button id="demo-button3" class="deque-button" data-tooltip="This is a non-functional demo button with a tooltip">Button 3</button>
</p>
JavaScript Source Code
class Tooltip {
static elements = [];
constructor(domNode, options) {
var self = this;
this.showOnFocus = true;
this.keepTooltipOnMouseOver = true;
this.tooltipMouseOver = false;
if ( typeof options != "object" ) options = {};
if( (typeof domNode != "object" ) && (typeof domNode.nodeName === "undefined") )
{
console.log(langText.errorNodeNotObject);
return;
}
var tooltipText = domNode.getAttribute("data-tooltip");
if( !tooltipText )
{
console.log(langText.errorNoTooltip);
return;
}
var wrapperSpan = document.createElement("span");
domNode.parentNode.replaceChild(wrapperSpan, domNode);
wrapperSpan.appendChild(domNode);
var tooltipSpan = document.createElement("span");
wrapperSpan.appendChild(tooltipSpan);
wrapperSpan.classList.add("tooltip-wrapper");
tooltipSpan.classList.add("tooltip");
tooltipSpan.setAttribute("role", "tooltip");
tooltipSpan.id = crypto.randomUUID();
domNode.setAttribute("aria-describedby", tooltipSpan.id);
this.node = domNode;
this.tooltip = tooltipSpan;
if ( typeof options != "object" ) options = {};
if( (typeof options["styleClass"] === "string") && (options["styleClass"].trim() != "" ) )
tooltipSpan.classList.add(options["styleClass"]);
if(typeof options["showOnFocus"] === "boolean")
this.showOnFocus = options["showOnFocus"];
if(typeof options["keepTooltipOnMouseOver"] === "boolean")
this.keepTooltipOnMouseOver = options["keepTooltipOnMouseOver"];
this.hideTooltip();
tooltipSpan.innerHTML = '<span aria-label="Tooltip :"></span>'+ tooltipText;
domNode.addEventListener("mouseover", this.showTooltip.bind(this));
domNode.addEventListener("mousemove", this.showTooltip.bind(this));
domNode.addEventListener("mouseout", this.mouseOutHandler.bind(this) );
document.body.addEventListener("keydown", Tooltip.handleEscapeKey );
if (this.showOnFocus)
{
domNode.addEventListener("focus", this.showTooltip.bind(this));
domNode.addEventListener("blur", function() { if(!self.tooltipMouseOver) self.hideTooltip(); });
}
if(this.keepTooltipOnMouseOver)
{
this.tooltip.addEventListener("mouseover", function() { self.tooltipMouseOver = true; self.showTooltip(); });
this.tooltip.addEventListener("mousemove", function() { self.tooltipMouseOver = true; self.showTooltip(); });
this.tooltip.addEventListener("mouseout", this.hideTooltip.bind(this));
}
Tooltip.elements.push(this);
}
hideTooltip(escapeKey)
{
if((document.activeElement == this.node) && (escapeKey !== true) ) return;
this.tooltip.classList.add("hidden");
this.tooltip.classList.remove("visible");
this.tooltip.setAttribute("aria-hidden", "true");
}
showTooltip(event)
{
this.tooltip.classList.add("visible");
this.tooltip.classList.remove("hidden");
this.tooltip.setAttribute("aria-hidden", "false");
this.tooltip.style.top = (-10 - this.tooltip.offsetHeight) + "px";
}
mouseOutHandler(event)
{
var self=this;
var id = setTimeout( function(){
if( self.tooltipMouseOver)
{
self.tooltipMouseOver = false;
return;
}
self.hideTooltip();
}, 1000);
}
static handleEscapeKey(event)
{
if(event.key == "Escape")
{
var elements = Tooltip.elements;
for(var i =0; i < elements.length; i++)
elements[i].hideTooltip(true);
}
}
}
var tooltips = {};
var itemsToTip = document.querySelectorAll('[data-tooltip]');
var options = {};
//options.styleClass = "tooltip-custom-1";
//options.showOnFocus = false;
for (var i = 0; i < itemsToTip.length; i++) {
options.styleClass = null;
if( ((i+1) % 2) == 0 )
options.styleClass = "tooltip-custom-1";
tooltips[itemsToTip[i].id] = new Tooltip(itemsToTip[i], options);
}
CSS Source Code
.tooltip-wrapper {
position: relative;
}
.tooltip {
box-sizing: border-box;
font-size: 13px;
position: absolute;
background: #ffffff;
border: 1px solid rgba(0, 0, 0, 0.3);
min-width: 104px;
max-width: 400px;
margin-right: -340px;
padding: 8px 6px;
line-height: 16px;
z-index: 70;
top: -25px;
left: 80%;
}
.tooltip:after {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #868181;
top: 100%;
position: absolute;
bottom: 0;
height: 0;
width: 0;
left: -0.5%;
content: "";
}
.tooltip-custom-1 {
font-size: 16px;
font-family: 'Times New Roman', Times, serif;
padding: 16px;
background-color: #fff6a9;
}
.hidden { display: none;}
.visible { display: block;}
@media screen and (max-width: 700px) {
.tooltip {
left: 10%;
max-width: 200px;
}
}