
easy-tooltips is a zero-dependency JavaScript tooltip library that attaches custom tooltips to HTML elements through HTML data attributes.
The tooltip can stay with its trigger element, follow the pointer, or pin to a pointer position while the library adjusts placement near viewport edges.
Features
- Zero-dependency JavaScript and CSS.
- Data-attribute setup with no initialization call.
- Viewport-aware placement, side flipping, and edge shifting.
- Element, cursor, and pinned anchor modes.
- Mouse, touch, and keyboard focus activation.
- Plain text and sourced HTML tooltip content.
- CSS variables, custom classes, inline styles, gradients, and image surfaces.
- Open, close, and move events.
- Automatic updates for dynamically changed DOM content.
How to Use It
Browser Setup
Load the stylesheet before the JavaScript file.
<link href="https://cdn.jsdelivr.net/npm/easy-tooltips/dist/easy-tooltips.min.css" rel="stylesheet" > <script src="https://cdn.jsdelivr.net/npm/easy-tooltips/dist/easy-tooltips.min.js"></script>
Install with npm
Install the package from npm and import its stylesheet and JavaScript entry point.
# NPM npm install easy-tooltips
import "easy-tooltips/styles.css"; import "easy-tooltips";
Basic Usage
Set data-easy-tooltip on a trigger element and put plain text in the attribute value. Use a newline entity when one text tooltip needs multiple lines.
<button data-easy-tooltip="Save your current settings"> Save </button> <span tabindex="0" data-easy-tooltip="Account status Updated a few seconds ago" > Status </span>
Smart Tooltip Positioning
Each tooltip starts on its preferred side. The default is above. You can choose below, left, right, or entry with data-easy-tooltip-prefer. The entry value uses the edge where the pointer entered the trigger and uses above for keyboard focus.
When the preferred side lacks space, easy-tooltips tries the opposite side and shifts the tooltip along the available edge. An inside placement keeps the tooltip within the viewport when neither side has enough room. Placement is recalculated on scroll and resize events, and Visual Viewport dimensions are used when that browser API is available.
<button data-easy-tooltip="Opens below when space is available" data-easy-tooltip-prefer="below" > Below </button> <button data-easy-tooltip="Uses the pointer entry edge" data-easy-tooltip-prefer="entry" > Entry side </button>
Cursor and Pin Anchor Modes
data-easy-tooltip-anchor accepts element, cursor, cursor-x, cursor-y, pin, pin-x, and pin-y. The default element mode uses the trigger box. Cursor modes track pointer coordinates. Pin modes freeze the selected pointer coordinate when the tooltip appears.
The full pin mode snaps to the nearest trigger edge. Its placement follows the pointer entry side unless data-easy-tooltip-prefer specifies another side. Keyboard focus uses element anchoring because it has no pointer coordinates.
<button data-easy-tooltip="Tracks the pointer" data-easy-tooltip-anchor="cursor" > Inspect </button> <button data-easy-tooltip="Pins to the nearest trigger edge" data-easy-tooltip-anchor="pin" > Pin tooltip </button> <button data-easy-tooltip="Tracks horizontal pointer movement" data-easy-tooltip-anchor="cursor-x" > Track X axis </button>
Keep a Tooltip Open During Pointer Interaction
Pressing a supported control keeps its tooltip visible until pointer release. The default applies to inputs, selects, textareas, buttons, and common button, slider, checkbox, radio, and switch roles.
Set data-easy-tooltip-hold on another trigger to opt in. Set data-easy-tooltip-hold="false" on a supported control to disable this behavior.
<div data-easy-tooltip="Stays visible while dragging" data-easy-tooltip-hold > Drag handle </div> <button data-easy-tooltip="Hides after pointer exit" data-easy-tooltip-hold="false" > Press </button>
Custom HTML Content
Use data-easy-tooltip-src when a tooltip needs formatted HTML. Its value can reference an element by ID or CSS selector, or use next and prev for adjacent content.
<button data-easy-tooltip-src="#shipping-details"> Shipping details </button> <template id="shipping-details"> <strong>Free shipping</strong><br> Orders usually arrive in 2 to 4 business days. </template>
The adjacent-source forms hide the matching sibling automatically.
<button data-easy-tooltip-src="next">
View ingredients
</button>
<div>
<ul>
<li>Oats</li>
<li>Honey</li>
<li>Sea salt</li>
</ul>
</div>
<div>
<strong>Delete warning</strong><br>
This action removes the saved item.
</div>
<button data-easy-tooltip-src="prev">
Delete
</button>Custom Classes and Per-Tooltip Styles
Use data-easy-tooltip-class for reusable visual variants. The attribute accepts one or more class names.
<button data-easy-tooltip="Settings saved" data-easy-tooltip-class="success-tooltip" > Save </button> <button data-easy-tooltip="This action cannot be undone" data-easy-tooltip-class="danger-tooltip bold-tooltip" > Delete </button>
.success-tooltip {
--easy-tooltip-background-color: #f0fdf4;
--easy-tooltip-border-color: #27ae60;
--easy-tooltip-text-color: #166534;
}
.danger-tooltip {
--easy-tooltip-background-color: #fef2f2;
--easy-tooltip-border-color: #e74c3c;
--easy-tooltip-text-color: #991b1b;
}
.bold-tooltip {
font-weight: 700;
}For a one-off visual change, data-easy-tooltip-style applies inline CSS to the generated tooltip.
<span tabindex="0" data-easy-tooltip="Pill-shaped tooltip" data-easy-tooltip-style="--easy-tooltip-border-radius: 999px; --easy-tooltip-padding: 8px 16px;" > Account </span>
Gradient and Image Backgrounds
--easy-tooltip-background and --easy-tooltip-border accept CSS image values such as gradients and url() images. The related data attributes set these values on individual tooltips.
.brand-tooltip {
--easy-tooltip-background: linear-gradient(135deg, #667eea, #764ba2);
--easy-tooltip-border: linear-gradient(135deg, #f0f, #0ff);
--easy-tooltip-border-size: 2px;
--easy-tooltip-text-color: #fff;
}<button
data-easy-tooltip="Gradient border"
data-easy-tooltip-border="linear-gradient(135deg, #f0f, #0ff)"
>
Details
</button>
<button
data-easy-tooltip="Image background"
data-easy-tooltip-background="url('tooltip-texture.jpg')"
>
Preview
</button>All HTML Data Attribute
| Attribute | Description |
|---|---|
data-easy-tooltip | Sets plain text tooltip content. Newlines are supported. |
data-easy-tooltip-src | Copies HTML content from an element ID, CSS selector, next, or prev. |
data-easy-tooltip-class | Applies one or more class names to the generated tooltip. |
data-easy-tooltip-style | Applies inline CSS to one generated tooltip. |
data-easy-tooltip-prefer | Sets above, below, left, right, or entry as the preferred placement. |
data-easy-tooltip-anchor | Sets element, cursor, single-axis cursor, pin, or single-axis pin anchoring. |
data-easy-tooltip-hold | Keeps the tooltip open during a pointer press, or disables automatic hold behavior when set to false. |
data-easy-tooltip-background | Sets a CSS image or gradient background for one tooltip. |
data-easy-tooltip-border | Sets a CSS image or gradient border for one tooltip. |
CSS Variables
Override these custom properties on :root for a global theme or on a tooltip class for a local variant.
Appearance
| CSS Variable | Description |
|---|---|
--easy-tooltip-background-colorDefault: #fff | Base tooltip fill color. |
--easy-tooltip-text-colorDefault: #000 | Tooltip text color. |
--easy-tooltip-border-colorDefault: #aaa | Base border color. |
--easy-tooltip-border-sizeDefault: 1px | Border thickness. |
--easy-tooltip-border-radiusDefault: 4px | Tooltip corner radius. |
--easy-tooltip-paddingDefault: 8px 12px | Inner spacing around tooltip content. |
--easy-tooltip-max-widthDefault: 100% | Maximum tooltip width. |
--easy-tooltip-backgroundDefault: none | CSS image or gradient used for the tooltip body and arrow. |
--easy-tooltip-borderDefault: none | CSS image or gradient used for the tooltip border. |
Positioning
| CSS Variable | Description |
|---|---|
--easy-tooltip-distanceDefault: 16px | Gap between the anchor and tooltip. |
--easy-tooltip-viewport-paddingDefault: 16px | Minimum spacing from viewport edges. |
--easy-tooltip-arrow-sizeDefault: 16px | Arrow width. One value uses half that value for height. Two values set width and height separately. Set the value to 0 to hide the arrow. |
--easy-tooltip-arrow-edge-buffer-xDefault: 12px | Horizontal arrow spacing from rounded corners for above and below tooltips. |
--easy-tooltip-arrow-edge-buffer-yDefault: 6px | Vertical arrow spacing from rounded corners for left and right tooltips. |
--easy-tooltip-arrow-radiusDefault: 0 | Radius applied to the SVG arrow tip. |
Animation and Delay
| CSS Variable | Description |
|---|---|
--easy-tooltip-animation-lengthDefault: 0.15s | Fade and movement transition duration. |
--easy-tooltip-animation-distanceDefault: 4px | Distance used for the entrance movement. |
--easy-tooltip-delayDefault: 0s | Base delay applied before each tooltip opens. |
--easy-tooltip-inactive-delayDefault: 0.15s | Extra delay used when no tooltip was recently active. |
--easy-tooltip-cooldownDefault: 0.15s | Time after the last tooltip closes before the inactive delay applies again. |
Customize the Global Theme
This theme changes the main colors, width, spacing, arrow geometry, and timing values in one place.
:root {
--easy-tooltip-background-color: #111827;
--easy-tooltip-text-color: #f9fafb;
--easy-tooltip-border-color: #374151;
--easy-tooltip-border-size: 1px;
--easy-tooltip-border-radius: 6px;
--easy-tooltip-padding: 8px 12px;
--easy-tooltip-max-width: 320px;
--easy-tooltip-distance: 12px;
--easy-tooltip-viewport-padding: 12px;
--easy-tooltip-arrow-size: 14px 7px;
--easy-tooltip-arrow-radius: 2px;
--easy-tooltip-animation-length: 0.15s;
--easy-tooltip-animation-distance: 4px;
--easy-tooltip-delay: 0s;
--easy-tooltip-inactive-delay: 0.2s;
--easy-tooltip-cooldown: 0.15s;
}Tooltip Events
Listen on a trigger element or on document when application code needs to react to tooltip visibility or placement changes. The custom events bubble from the trigger.
// Fires after the tooltip actually appears.
document.addEventListener("easy-tooltip-open", (event) => {
console.log(event.detail.side, event.detail.point);
});
// Fires when the tooltip starts hiding.
document.addEventListener("easy-tooltip-close", (event) => {
console.log(event.detail.trigger);
});
// Fires after the tooltip changes side or anchor position.
document.addEventListener("easy-tooltip-move", (event) => {
console.log(event.detail.previous, event.detail.rect);
});Event Detail
| Property | Description |
|---|---|
trigger | The element associated with the tooltip. |
tooltip | The generated tooltip element. |
text | The tooltip text content. |
side | The current above, below, left, or right placement. |
inside | true when the tooltip uses the inside fallback placement. |
anchor | The active anchor mode. |
anchorRect | The viewport-coordinate box used as the anchor. |
point | The viewport-coordinate point targeted by the tooltip arrow. |
rect | The current tooltip box in viewport coordinates. |
previous | Previous placement data on easy-tooltip-move. |
Alternatives & Related Resources
- Customizable Interactive Tooltips In Pure JavaScript – Tippy.js
- Accessible Rich-text Tooltips with Auto-Positioning – webcimes-tooltip
- Lightweight Tooltips with CSS Variable Styling – ez-tip
FAQs
Q: Why does a tooltip on a span or div not open from the keyboard?
A: Plain spans and divs are not focusable by default. Add tabindex="0" when the trigger should receive keyboard focus.
Q: How do I place HTML inside a tooltip?
A: Put the formatted content in a template or nearby element and reference it with data-easy-tooltip-src. The value accepts an element ID, CSS selector, next, or prev.
Q: What happens when a tooltip does not fit on its preferred side?
A: The positioning logic tries the opposite side and shifts the tooltip along the available edge. An inside placement is used when neither side has enough room.
Q: Can application code react when a tooltip moves?
A: Yes. Listen for easy-tooltip-move on the trigger or a parent such as document. Its event detail includes current placement data and the previous placement.
Changelog
v3.5.4 (09/22/2026)
- Bugfixes
v3.5.2 (08/31/2026)
- Tooltips now hold their opening spot while their trigger scrolls, only following it along the axis the arrow points and clamping to the trigger’s edge once it reaches them. Before they tracked the trigger on both axes, which made them jitter in scrollable lists.
- Bugfixes.
v3.5.1 (08/23/2026)
- Added
easy-tooltip-open,easy-tooltip-close, andeasy-tooltip-moveevents. - Expanded data-attribute controls for anchor and interaction behavior.
- Updated pin anchoring to snap to the nearest trigger edge when appropriate.
- Updated pin timing to capture the anchor point when the tooltip appears.
- Fixed positioning and interaction bugs.
v3.3.0 (08/20/2026)
- Added
data-easy-tooltip-stylefor per-tooltip inline styling.
v3.2.0 (08/20/2026)
- Added
data-easy-tooltip-src="prev". - Added Popover API top-layer rendering where supported.
v3.1.0 (07/21/2026)
- Added image and gradient backgrounds through
--easy-tooltip-background. - Added image and gradient borders through
--easy-tooltip-border.
v3.0.0 (05/24/2026)
- Added element, cursor, and pin anchor modes.
- Added skip-delay timing for movement between adjacent tooltips.
- Changed the tooltip body and arrow to a single SVG path.
- Added newest-on-top stacking for simultaneous tooltips.
- Changed the default arrow size to
16px. - Changed
--easy-tooltip-delayto0sand moved the initial wait to--easy-tooltip-inactive-delay.
v2.0.0 (05/22/2026)
- Expanded and renamed the tooltip data attributes.
- Fixed multiple tooltip behavior issues.
v1.5.0 (03/16/2026)
- Added MutationObserver support for dynamically changed tooltip triggers.
v1.4.3 (01/26/2026)
- Added newline support and configurable tooltip delays.
- Updated custom HTML handling to use source elements and reject raw HTML strings.







