useTextSelection
Hook to track text selection.
See: Selection API.
Demo
INFO
The component renders a grid with two columns. First column contains two tag p elements with text and secondo column the result of useTextSelection hook.
useTextSelection is initialized with a target property that has a ref attached to first column div and onEnd property that has a function that clean selection. When text is selected appears colored rectangles with the coordinates returned from hook.
Loading demo…
Show source code
tsx
import { useMemo, useRef } from "react";
import { useTextSelection } from "../../../.."
export const UseTextSelection = () => {
const ref = useRef<HTMLDivElement>(null);
const selection = useTextSelection({ target: ref, onEnd: () => {getSelection()?.removeAllRanges()} });
const rectangles = useMemo(() => {
if (!selection) {
return null;
} else {
const rectangles = [];
rectangles.push(
<div
key="outside-rectangle"
style={{
position: "absolute",
border: ".5px solid red",
top: selection.outsideRectangle.top + "px",
left: selection.outsideRectangle.left + "px",
width: selection.outsideRectangle.width + "px",
height: selection.outsideRectangle.height + "px",
}}
></div>
);
selection.innerRectangles.forEach((el, index) => {
rectangles.push(<div
key={"inner-rectangle-"+index}
style={{
position: "absolute",
border: ".5px solid darkcyan",
top: el.top + "px",
left: el.left + "px",
width: el.width + "px",
height: el.height + "px",
}}
></div>);
})
return rectangles;
}
}, [selection]);
return <div style={{ display: "grid", gridTemplateColumns: "50% 50%", columnGap: 15 }}>
<div ref={ref} style={{ position: "relative", border: "1px solid lightgray" }}>
<div>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt repudiandae fugit distinctio molestiae excepturi ex qui, impedit iste odit. Explicabo quis reprehenderit voluptates reiciendis nostrum minima autem temporibus sint doloribus</p>
</div>
<div>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt repudiandae fugit distinctio molestiae excepturi ex qui, impedit iste odit. Explicabo quis reprehenderit voluptates reiciendis nostrum minima autem temporibus sint doloribus</p>
</div>
{rectangles}
</div>
<div style={{textAlign: "left", padding: "0 1em", maxHeight: 300, overflow: "auto", border: "1px solid lightgray"}}>
<p><strong>Selection:</strong></p>
<pre>{JSON.stringify(selection, null, 2)}</pre>
</div>
</div>
}Types
UseTextSelectionProps
Parameters accepted by useTextSelection.
| Property | Type | Required | Description |
|---|---|---|---|
target | RefObject<HTMLElement> \| HTMLElement | The element within which text selection is tracked. Accepts either a React RefObject<HTMLElement> or a direct HTMLElement reference. When omitted, selection is tracked globally on document.body. | |
onStart | (evt: Event) => void | Called when the user begins a selection gesture inside the target element (on pointerdown). Also fired when the pointer re-enters the target while a selection is already in progress and no text is currently selected. | |
onChange | (evt: Event) => void | Called on every selectionchange event while a selection gesture is active inside the target element. Registered on document for the duration of the gesture and removed on pointerup or pointerleave. | |
onEnd | (evt: Event) => void | Called when the selection gesture ends inside the target element (on pointerup or pointerleave), after the final {@link TextSelection} value has been computed and the reactive state has been updated. |
UseTextSelectionResult
Text selection state returned by useTextSelection. Updated whenever the user changes the text selection in the document.
ts
export type UseTextSelectionResult = null | {
/** The raw text content of the current selection (equivalent to `Selection.toString()`). */
text: string;
/**
* Direction of the selection relative to the anchor point.
* - `"forward"` — the user dragged from left to right / top to bottom.
* - `"backward"` — the user dragged from right to left / bottom to top.
*/
direction: "forward" | "backward";
/**
* Bounding rectangle that fully encloses the entire selection, even when it spans
* multiple lines or nodes. Equivalent to the union of all `innerRectangles`.
*/
outsideRectangle: DOMRect;
/**
* Array of `DOMRect` objects, one per text line or range fragment.
* Use these for fine-grained positioning (e.g. rendering a floating toolbar aligned
* to each line of a multi-line selection).
*/
innerRectangles: DOMRect[];
}