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-text=<<text>> data-mode=<<mode>> />
\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.
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.