Vaadin - HTML Tooltip
The default Vaadin Tooltip Component doesn’t allow HTML contents by default. But, with a bit of hacking, it can be made to show HTML. The usual warning: if not careful, showing unescaped HTML contents may cause your app susceptible to XSS vulnerabilities.
Popover
When on Vaadin 24+, it’s much better to use the new Popover
component, since it
can attach to any Vaadin component - Tooltip only works with components that implement
the HasTooltip
interface. See below for a bunch of tips. But, if you’re on Vaadin 23,
read on.
Browser Built-in Tooltip
You can set a tooltip to any Vaadin component, simply by calling:
component.getElement().setAttribute("title", description);
The upside is that this works with any component; the downside is that you can’t use HTML, and you can’t control the tooltip in any way: you can’t change the delay, the positioning (above/below), the color, show-on-hover VS show-on-click.
Hacking Vaadin Tooltip
This code works in Vaadin 23+:
@Route("")
public class MainRoute extends VerticalLayout {
public MainRoute() {
final Button b = new Button("OK");
Tooltip tooltip = setTooltipHtml(b, "This is a <b>tooltip</b>");
tooltip.setPosition(Tooltip.TooltipPosition.START);
add(b);
}
public static Tooltip setTooltipHtml(HasTooltip component, String html) {
final Tooltip tooltip = component.setTooltipText(html);
final Element tooltipElement;
try {
final Field f = Tooltip.class.getDeclaredField("tooltipElement");
f.setAccessible(true);
tooltipElement = ((Element) f.get(tooltip));
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
tooltipElement.executeJs("""
function htmlTooltipRenderer(root) {
var contentInHTML = this.text;
if (contentInHTML !== undefined) {
root.innerHTML = contentInHTML;
} else {
root.textContent = "";
}
};
this._renderer = htmlTooltipRenderer.bind(this);
""");
return tooltip;
}
}
Thanks Samuli Penttilä for this awesome tip!
Tooltip with any Vaadin Component
Making a component implement HasTooltip
won’t cause the tooltip to magically start working:
the component client-side needs to support the tooltip machinery as well. However,
you can nest the component in a CustomField
which does support tooltip.
An example of a wrapper class (which works as a Div
replacement) follows:
public class ComponentWithTooltip extends CustomField<Void> implements HasOrderedComponents, ClickNotifier<ComponentWithTooltip> {
public ComponentWithTooltip() {
setWidthFull();
}
@Override
protected Void generateModelValue() {
return null;
}
@Override
protected void setPresentationValue(Void newPresentationValue) {
}
@Override
public void add(Component... components) {
HasOrderedComponents.super.add(components);
}
@Override
public void remove(Component... components) {
HasOrderedComponents.super.remove(components);
}
}
Rich Tooltip
Alternatively, try out the rich-tooltip from Tatu.