Tooltips and Popovers
yFiles for HTML provides a flexible popover system that covers everything from simple, non-interactive tooltips to fully interactive pop-up panels. All popovers are rendered in view coordinates (not scrolled with the graph) and may either be plain string content or rich HTML elements.
There are three kinds of popovers, distinguished by their PopoverBehavior:
| Behavior | Light-dismissable | Multiple at once | Description |
|---|---|---|---|
Yes |
No (only one) |
Corresponds to a classic tooltip. Shows informational content without interaction. Closes automatically when clicking outside or pressing Escape. |
|
Yes |
No (but can coexist with a Hint) |
An interactive popover that can contain UI elements such as buttons. Closes automatically when clicking outside or pressing Escape. |
|
No |
Yes (unlimited) |
The developer has full control over when and how many are shown. Not light-dismissable — a close mechanism (e.g., a close button) must be provided by the developer. |
|
Note
|
Light-dismissable means a popover closes automatically when the user clicks outside of it or presses Escape. Only MANUAL popovers are not light-dismissable. |
By default, all popovers are rendered in the HTML top layer and may extend beyond the component bounds.
This can be changed by setting the useTopLayer to false.
The ToolTipInputMode
Tooltips are supported by both GraphEditorInputMode and GraphViewerInputMode.
The most convenient way to provide a tooltip for a graph item is to use a handler for GraphEditorInputMode’s or GraphViewerInputMode’s query-item-tool-tip event:
// display tooltips for nodes
graphEditorInputMode.toolTipItems = GraphItemTypes.NODE
// register a listener
graphEditorInputMode.addEventListener(
'query-item-tool-tip',
(eventArgs) => {
if (eventArgs.handled) {
// A tooltip has already been assigned => nothing to do.
return
}
// We can safely assume a node here because we set toolTipItems to only NODE
const hitNode = eventArgs.item ? (eventArgs.item as INode) : null
if (hitNode && hitNode.labels.size > 0) {
// Setting the tooltip also sets the 'handled' property to 'true'
eventArgs.toolTip = hitNode.labels.get(0).text
eventArgs.handled = true // also happens automatically, when ToolTip is assigned
}
}
)The toolTipItems property on GraphEditorInputMode and GraphViewerInputMode controls which items trigger the tooltip events when clicked. You can obtain a more specific distinction per item by setting a custom queryToolTipPredicate. Typically, performing the logic in the event handler is the recommended approach.
The actual work is done by the ToolTipInputMode, which is a child input mode for both GraphEditorInputMode and GraphViewerInputMode. It delegates the management of the currently open tooltips and popovers to query-item-tool-tip
Further customizations can be done on the ToolTipInputMode. GraphEditorInputMode and GraphViewerInputMode trigger the query-item-tool-tip upon ToolTipInputMode’s query-tool-tip event.
The validHoverLocationHitTestable specifies the area in which a tooltip will be queried.
The QueryItemToolTipEventArgs<TModelItem> of the query-tool-tip event allows setting the content for the tooltip and further customization of the popover through the popover property:
graphEditorInputMode.addEventListener('query-item-tool-tip', (args) => {
if (args.item instanceof INode) {
const node = args.item
args.toolTip = 'Node'
// anchor the tooltip's top center corner to the node's bottom center
args.popover.anchor = new Point(
node.layout.x + node.layout.width / 2,
node.layout.bottomLeft.y
)
args.popover.ratios = new Point(0.5, 0) // 0.5 is the x-ratio (center), 0 is the y-ratio (top)
// move 10 pixels below the node (in view coordinates)
args.popover.offset = new Point(0, 10)
// keep it open for 5 seconds, max - overrides default
args.popover.duration = '5s'
args.handled = true
}
})Secure Content with Trusted Types
If the provided content on the QueryItemToolTipEventArgs<TModelItem> is a string, the library internally adds this to the
parentElement as innerHTML content. While this allows for rich formatting, it creates a
potential Cross-Site Scripting (XSS) vulnerability if you pass unvalidated user data into that property. So if your site
uses a strict Content Security Policy, the browser will throw an error due to this injection sink.
To mitigate this, you can use the Trusted Types API by passing sanitized strings instead of pure plain strings.
For example, enabling the following CSP defines the "tooltip-policy" as a trusted type for injection sinks, disallowing any other strings:
<meta
http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; trusted-types tooltip-policy;"
/>Then, when setting a string tooltip that triggers the library’s innerHTML assignment, you can mark it as trusted by
using a tooltip-policy:
// create a trusted types policy for sanitizing tooltips
const tooltipSanitizer = trustedTypes.createPolicy('tooltip-policy', {
createHTML: (input) => DOMPurify.sanitize(input)
})
graphEditorInputMode.addEventListener('query-item-tool-tip', (evt) => {
if (evt.handled) return
// the following throws due to the strict CSP and the library's innerHTML assignment
// evt.toolTip = 'My Tooltip'
// use the tooltip policy on the string content to mark it as secure
evt.toolTip = tooltipSanitizer.createHTML('My Tooltip')
// alternatively, use HTML elements for tooltips to not trigger the library's innerHTML assignment
// const tooltip = document.createElement('span')
// tooltip.innerText = 'My Tooltip'
// evt.toolTip = tooltip
evt.handled = true
})Styling of the Tooltip or Popover Element
The basic styling of the tooltip/popover element can be performed using the yfiles-tooltip or yfiles-popover CSS class. Using this
CSS class, you can customize features like the background, border, margin, or font. For example, to style a tooltip with a light-gray background and
a black border, you can use the following rules:
.yfiles-tooltip {
background-color: lightgray;
border: 2px solid black;
}CSS Transition or Animation for Popovers in Top-Layer
|
Note
|
The CSS animations described here apply only to tooltips / popover elements that are added
to the HTML’s top-layer, i.e., when the useTopLayer is set to
true (which is the default setting).
|
To animate popover elements that are added with the HTML Popover API, you can use the CSS specific
popover pseudoclasses in combination with the .yfiles-popover__container CSS class of the popover
container.
For example, to lengthen the fade-in/-out animation, you could define the following rules:
.yfiles-popover__container[popover] {
transition:
opacity 500ms ease,
display 500ms allow-discrete;
}Additionally, the popover containers for tooltips (provided by ToolTipInputMode) have the
.yfiles-popover—tooltip CSS class, while the context menu (provided by ContextMenuInputMode)
uses .yfiles-popover—context-menu. This makes it easy to adjust animations for specific popovers
or for all of them at once.
CSS Transition or Animation for Popovers in parentElement
|
Note
|
The CSS animation phases described here apply only to tooltips / popover elements that are not added
to the HTML’s top-layer, i.e., when the useTopLayer is set to false.
|
You can use CSS transitions or animations to control how the tooltip element appears and disappears by leveraging different CSS classes that are applied during the enter and leave phases.
You can disable the transition or animation for either phase by not defining the related CSS classes, as explained in the following sections.
Enter Phase
The following CSS classes are applied when the tooltip is added to the DOM:
-
yfiles-popover__container-entering: This CSS class is present during the entire enter phase and can be used to define CSS transition or animation functions. It is removed when the enter transition or animation ends. -
yfiles-popover__container-enter: Initializes the start state of the tooltip. The class is added before the element is inserted into the DOM and removed immediately after the element is added to the DOM. -
yfiles-popover__container-enter-to: Defines the end state of the tooltip. This class is added whenyfiles-popover__container-enteris removed (i.e., immediately after the element enters the DOM), and it is removed when the CSS transition or animation ends.
By default, yFiles provides a simple fade transition for the enter phase of tooltips:
.yfiles-popover__container-entering {
transition: opacity 0.2s ease-in;
}
.yfiles-popover__container-enter {
opacity: 0;
}
.yfiles-popover__container-enter-to {
opacity: 1;
}Alternatively, you could also define a CSS animation, for example:
@keyframes fade {
from { opacity: 0 }
to { opacity: 1 }
}
.yfiles-popover__container-entering {
animation: fade 0.2s ease-in;
}Leave Phase
The following CSS classes are applied when the tooltip leaves the DOM:
-
yfiles-popover__container-leaving: This CSS class is present during the entire leave phase and can be used to define CSS transition or animation functions. It is removed when the leave transition or animation ends. -
yfiles-popover__container-leave: Initializes the beginning state of the tooltip as it leaves. The class is added when the leave phase begins and is removed immediately afterward, whenyfiles-popover__container-leave-tois set. -
yfiles-popover__container-leave-to: Defines the end state of the tooltip before it is removed from the DOM. This class is added whenyfiles-popover__container-leaveis removed and removed when the CSS transition or animation ends. Thetransitionendoranimationendevent also determines when the element is removed from the DOM.
By default, yFiles provides a simple fade transition for the leave phase of tooltips:
.yfiles-popover__container-leaving {
transition: opacity 0.2s ease-out;
}
.yfiles-popover__container-leave {
opacity: 1;
}
.yfiles-popover__container-leave-to {
opacity: 0;
}Alternatively, you could also define a CSS animation, for example:
.yfiles-popover__container-leaving {
animation: fade reverse 0.2s ease-out;
}Integrate Custom Components
You can fully customize the content displayed within a tooltip with the toolTip property. Besides plain text string, you can also assign an HTMLElement, which will be directly inserted into the tooltip’s display area. This enables embedding rich HTML structures and integrating dynamically rendered components from third-party libraries like React, Vue, or Angular.
To embed a custom component, create a container div element, render your third-party component into this container,
and then assign the container element to args.toolTip within your event handler.
For concrete examples, refer to the dedicated demos: React, Angular, Vue.
The PopoverManager
Every GraphInputMode (i.e., both GraphEditorInputMode and GraphViewerInputMode) provides a popoverManager property. The PopoverManager is a utility class that manages popovers described by PopoverDescriptor instances.
The ToolTipInputMode uses the PopoverManager internally to display its tooltips as HINT popovers. Custom code can use the PopoverManager directly to open, manage, and close popovers of any behavior.
To open a popover, create a PopoverDescriptor, configure its content, behavior, and anchor, and then call open:
const descriptor = new PopoverDescriptor({
content: 'Pop Over!',
behavior: 'manual',
duration: '5s',
anchor: graphComponent.viewPoint,
ratios: new Point(0, 0),
offset: new Point(5, 5)
})
await graphEditorInputMode.popoverManager.open(descriptor)The openPopovers property provides a live view of all currently open popover descriptors. The closeAll method immediately closes all popovers, including MANUAL ones.
Popover Coexistence Rules
When a new popover is opened, the PopoverManager enforces the following rules:
-
Opening a HINT popover closes any previously open Hint popover, but does not close Auto or Manual popovers.
-
Opening an AUTO popover closes any previously open Auto popover, but does not close Hint or Manual popovers. This means one Hint and one Auto popover can be visible at the same time.
-
MANUAL popovers are never implicitly closed and do not affect other popovers. Multiple Manual popovers can coexist.
Requerying Popovers
Once a popover is open, conditions may change – the viewport may scroll, the user may move the mouse, or the underlying data may be updated. The PopoverManager automatically triggers a requery on each active PopoverDescriptor whenever something significant changes, such as a viewport change or pointer movement.
This requery is communicated via the update event on the PopoverDescriptor. Registered event handlers can use this event to:
-
Decide whether the popover should remain open or be closed.
-
Update the popover’s content.
-
Change the popover’s anchor position and alignment.
The event argument carries the PopoverDescriptor itself and the
reason indicating why the requery was triggered. Most importantly, it provides a
showPopover boolean property that listeners
can set to indicate whether the popover should stay open. For HINT
popovers, this defaults to false, meaning the popover will close unless a listener explicitly sets it to true.
// Listen to update events to control popover lifetime
descriptor.addEventListener('update', (args) => {
if (args.reason === PopoverUpdateReason.VIEWPORT_CHANGED) {
args.showPopover = true
args.handled = true
return
}
// Keep the popover open as long as the mouse is within 100px of the anchor
const distance =
args.queryLocation.distanceTo(descriptor.anchor!) * args.context.zoom
args.showPopover = distance < 100
args.handled = true
// Optionally update the content or anchor
// descriptor.anchor = newAnchorPosition;
})Keeping Tooltips Open over Items
The GraphEditorInputMode and GraphViewerInputMode provide built-in handling for tooltip requerying. By default, when the ToolTipInputMode opens a HINT popover for a graph item, the tooltip stays open and its content is not requeried as long as the mouse pointer remains over the same item.
This behavior can be changed by listening for the update
event and setting showPopover to false. This closes the tooltip
as soon as the pointer moves which corresponds to the old tooltip default behavior:
graphEditorInputMode.addEventListener(
'query-item-tool-tip',
(eventArgs) => {
if (eventArgs.handled) {
// A tooltip has already been assigned by another listener -> nothing to do.
return
}
eventArgs.handled = true
eventArgs.toolTip = 'Legacy ToolTip Behavior'
// Close the tooltip for any update, e.g., pointer-moved
eventArgs.popover.addEventListener('update', (updateArgs) => {
updateArgs.showPopover = false
})
}
)Use Cases for Popovers
Typical use cases for AUTO and MANUAL popovers include:
-
Contextual toolbars: Displaying a toolbar with action buttons next to a selected graph element.
-
Detail panels: Showing interactive detail information – such as scrollable lists or forms – similar to a rich tooltip, but with the ability for the user to interact with the content.
-
Persistent annotations: Keeping informational panels open alongside one or more diagram elements using Manual mode.
Interactive popovers are typically opened in response to an active gesture such as clicking a graph element. For example, a developer can listen for item clicks on GraphEditorInputMode and open an Auto popover to present a contextual action menu beside the clicked node.