Turns out, there are a couple of cases where the tooltip might not show as expected. Mainly, if some other page element overlaps the area where the tooltip would show, so that it becomes obscured. A high z-index doesn’t always help if there are CSS block formatting contexts involved.
I’ve had this happen with tooltips in a left-side sidebar that get clipped by the story river.
A few iterations later, here is a more universal solution.
Taking a lesson from Implementing a contextmenu with wikitext, we can just put the $reveal containing the tooltip into the PageTemplate and set its state with our various $eventcatchers (one per element with a tooltip). The neat thing is that we no longer need to care about making the state tiddler names unique. Only one tooltip will ever be shown at a time, so we only need one state tiddler for our one dynamic tooltip.
The new tiddler becomes:
list-after: $:/core/ui/PageTemplate/story
tags: $:/tags/PageTemplate
title: $:/yaisog/ui/PageTemplate/tooltip
type: text/vnd.tiddlywiki
\whitespace trim
<$let popupState="$:/state/tooltip"
tooltipText={{{ [<popupState>get[data-text]] }}}
mode={{{ [<popupState>get[data-mode]] }}} >
<$reveal state=<<popupState>> type="popup" tag="div" clamp="both">
<$eventcatcher
$mouseenter="<$action-timeout clear={{$:/temp/volatile/timers/hide-tooltip}} />"
$mouseleave="""<$action-timeout delay=100 actions="<$action-deletetiddler $tiddler=<<popupState>> />" tid="$:/temp/volatile/timers/hide-tooltip" />""" >
<div class="tooltip-content">
<$transclude $variable="tooltipText" $mode=<<mode>> />
</div>
</$eventcatcher>
</$reveal>
</$let>
and we remove the $reveal from the widget definition:
tags: $:/tags/Global
title: $:/yaisog/widgets/tooltip
type: text/vnd.tiddlywiki
\whitespace trim
\widget $.tooltip(text:"", mode:"inline")
\function .bottom-edge() [<event-fromviewport-posy>subtract<event-fromselected-posy>add<tv-selectednode-height>]
\function .right-edge() [<event-fromviewport-posx>subtract<event-fromselected-posx>add<tv-selectednode-width>]
\procedure show-tooltip()
<$action-popup $state=<<popupState>> />
<$action-deletetiddler $tiddler=<<popupState>> />
<$action-popup $state=<<popupState>> $coords={{{ [[@(]] [.right-edge[]] [[,]] [.bottom-edge[]] [[,0,0)]] +[join[]] }}} />
<$action-setfield $tiddler=<<popupState>> data-mode=<<mode>> />
<$wikify name="wikifiedText" text=<<text>> type={{!!type}} mode=<<mode>> output="html">
<$action-setfield $tiddler=<<popupState>> data-text=<<wikifiedText>> />
</$wikify>
\end show-tooltip
\procedure hide-tooltip()
<$action-deletetiddler $tiddler=<<popupState>> />
\end hide-tooltip
<% if [<text>!is[blank]] %>
<$let popupState="$:/state/tooltip">
<$eventcatcher style="display: inline-flex; align-items: center; width: auto; height: auto;"
$mouseenter="""<$action-timeout delay=500 actions="<<show-tooltip>>" tid="$:/temp/volatile/timers/show-tooltip" /><$action-timeout clear={{$:/temp/volatile/timers/hide-tooltip}} />"""
$mouseleave="""<$action-timeout delay=100 actions="<<hide-tooltip>>" tid="$:/temp/volatile/timers/hide-tooltip" /><$action-timeout clear={{$:/temp/volatile/timers/show-tooltip}} />""" >
<$slot $name="ts-raw" />
</$eventcatcher>
</$let>
<% endif %>
\end
The information about the tooltip text and mode is passed into the $reveal by setting corresponding fields in the state tiddler. I don’t really like that we need to $wikify the text for that, but the $reveal in the PageTemplate doesn’t e.g. know the variables within the triggering widget so it wouldn’t be able to render dynamically generated tooltip contents.
Using the various variables supplied by the $eventcatcher to its actions, we manually calculate the position of the bottom right corner of the element and place the tooltip there - using tv-popup-abs-coords
didn’t work for me in conjunction with scrolling.
The recyclable $reveal with the tooltip content also has an $eventcatcher the prevents the tooltip from disappearing when the mouse moves into it. Links within the tooltip will still work.
To create a tooltip, simply wrap the content that should receive the tooltip with the widget:
<$.tooltip text="This is a //tooltip//">This text has a tooltip</$.tooltip> while this has not.
I would have liked to set up a Sharing Edition link containing these tiddlers, but since we need the $action-timeout JavaScript widget and the modified $eventcatcher widget, it isn’t possible to share this way.