November 12, 2021
I'm using React.js in this example.
Let's start with the tooltip component. We'll use CSS and a bit of JS to show and hide the tooltip.
const Tooltip = ({ tooltipRef, showTooltip }) => (
<div ref={tooltipRef} className={`tooltip ${showTooltip ? 'tooltip--visible' : ''}`}>
Tooltip content
</div>
)
.tooltip {
height: 40px;
width: 150px;
padding: 5px;
background-color: #dcdcdc;
position: fixed;
visibility: hidden;
}
.tooltip--visible {
visibility: visible;
}
Now we'll create the main component with some text.
const App = () => {
const tooltipRef = React.useRef()
const [tooltipVisible, toggleTooltip] = React.useState(false)
return (
<div style={{ maxWidth: '600px', margin: 'auto' }}>
<Tooltip tooltipRef={tooltipRef} showTooltip={tooltipVisible} />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam neque urna, tincidunt a
scelerisque sed, placerat vitae lectus. Praesent velit nibh, egestas vitae laoreet non,
porttitor id erat. Pellentesque velit tortor, porttitor non magna et, accumsan euismod dui.
Duis eget nibh in turpis faucibus cursus. Praesent accumsan odio orci, in laoreet ipsum
porttitor auctor. Integer ligula sapien, varius eget augue ac, ornare dignissim lectus.
Fusce quis sapien lorem. Sed posuere viverra augue convallis feugiat. Aliquam elit ex,
dictum eu mauris in, vestibulum interdum augue. Donec ut lorem dapibus, pulvinar arcu at,
mattis sem. Aenean dictum, lacus vel bibendum lacinia, dui leo eleifend erat, eu eleifend
quam augue nec dolor
</p>
<p>
Suspendisse dictum lobortis nunc luctus convallis. Proin mauris elit, elementum ut cursus
eu, luctus condimentum ligula. Nulla malesuada nulla semper pharetra venenatis. Aliquam vel
lorem pulvinar, auctor erat nec, pretium tellus. Phasellus metus mauris, porta sed convallis
sit amet, lacinia eget neque. Quisque eu lacus vulputate, ultricies arcu quis, rutrum sem.
Proin est leo, iaculis in fermentum sit amet, elementum non metus. Cras rhoncus metus eget
magna bibendum aliquam. Phasellus suscipit turpis vel eros lacinia cursus. Curabitur nec ex
at risus bibendum maximus vel blandit sapien. Curabitur accumsan posuere urna imperdiet
vulputate. Quisque mattis diam nec quam aliquet interdum. Suspendisse nec turpis id purus
dapibus dapibus. Fusce dignissim quis libero congue auctor. Vestibulum vestibulum eget dui
vel bibendum. Nullam a vehicula neque.
</p>
</div>
)
}
Next, we'll need to add the mouseup
event listener.
...
const onMouseUp = () => {
//
}
useEvent('mouseup', onMouseUp)
...
useEvent
hook
import { useEffect } from 'react'
export default function useEvent(event, handler) {
useEffect(() => {
window.addEventListener(event, handler)
return () => {
window.removeEventListener(event, handler)
}
})
}
We'll use the Selection
object to determine the position of the tooltip.
...
const onMouseUp = () => {
const selection = window.getSelection()
const range = selection.getRangeAt(0)
toggleTooltip(!tooltipVisible)
positionTooltip(range)
}
useEvent('mouseup', onMouseUp)
...
postionTooltip
function
...
const positionTooltip = range => {
const rect = range.getClientRects()[0]
if (rect) {
const { left, top, width, height } = rect
const tooltip = tooltipRef.current
const selectionCenter = left + width / 2
const tooltipWidth = tooltip.offsetWidth
let tooltipLeft = selectionCenter - tooltipWidth / 2
tooltipLeft = tooltipLeft < 1 ? 0 : tooltipLeft
const tooltipTop = top + height + 10
tooltip.style.left = `${tooltipLeft}px`
tooltip.style.top = `${tooltipTop}px`
}
}
...
That's it.
import React from 'react'
import useEvent from './useEventHook'
const Tooltip = ({ tooltipRef, showTooltip }) => (
<div ref={tooltipRef} className={`tooltip ${showTooltip ? 'tooltip--visible' : ''}`}>
Tooltip content
</div>
)
const App = () => {
const tooltipRef = React.useRef()
const [tooltipVisible, toggleTooltip] = React.useState(false)
const positionTooltip = range => {
const rect = range.getClientRects()[0]
if (rect) {
const { left, top, width, height } = rect
const tooltip = tooltipRef.current
const selectionCenter = left + width / 2
const tooltipWidth = tooltip.offsetWidth
let tooltipLeft = selectionCenter - tooltipWidth / 2
tooltipLeft = tooltipLeft < 1 ? 0 : tooltipLeft
const tooltipTop = top + height + 10
tooltip.style.left = `${tooltipLeft}px`
tooltip.style.top = `${tooltipTop}px`
}
}
const onMouseUp = () => {
const selection = window.getSelection()
const range = selection.getRangeAt(0)
toggleTooltip(!tooltipVisible)
positionTooltip(range)
}
useEvent('mouseup', onMouseUp)
return (
<div style={{ maxWidth: '600px', margin: 'auto' }}>
<Tooltip tooltipRef={tooltipRef} showTooltip={tooltipVisible} />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam neque urna, tincidunt a
scelerisque sed, placerat vitae lectus. Praesent velit nibh, egestas vitae laoreet non,
porttitor id erat. Pellentesque velit tortor, porttitor non magna et, accumsan euismod dui.
Duis eget nibh in turpis faucibus cursus. Praesent accumsan odio orci, in laoreet ipsum
porttitor auctor. Integer ligula sapien, varius eget augue ac, ornare dignissim lectus.
Fusce quis sapien lorem. Sed posuere viverra augue convallis feugiat. Aliquam elit ex,
dictum eu mauris in, vestibulum interdum augue. Donec ut lorem dapibus, pulvinar arcu at,
mattis sem. Aenean dictum, lacus vel bibendum lacinia, dui leo eleifend erat, eu eleifend
quam augue nec dolor
</p>
<p>
Suspendisse dictum lobortis nunc luctus convallis. Proin mauris elit, elementum ut cursus
eu, luctus condimentum ligula. Nulla malesuada nulla semper pharetra venenatis. Aliquam vel
lorem pulvinar, auctor erat nec, pretium tellus. Phasellus metus mauris, porta sed convallis
sit amet, lacinia eget neque. Quisque eu lacus vulputate, ultricies arcu quis, rutrum sem.
Proin est leo, iaculis in fermentum sit amet, elementum non metus. Cras rhoncus metus eget
magna bibendum aliquam. Phasellus suscipit turpis vel eros lacinia cursus. Curabitur nec ex
at risus bibendum maximus vel blandit sapien. Curabitur accumsan posuere urna imperdiet
vulputate. Quisque mattis diam nec quam aliquet interdum. Suspendisse nec turpis id purus
dapibus dapibus. Fusce dignissim quis libero congue auctor. Vestibulum vestibulum eget dui
vel bibendum. Nullam a vehicula neque.
</p>
</div>
)
}
export default App