v137 · dom
Word Counter
Select any span of text — including across the ⬡ shadow-DOM inline elements — and get an instant word count, character count, and selection direction. The counter compares what getComposedRanges() sees versus the legacy getRangeAt(0), showing exactly where cross-shadow selections get truncated.
The Selection API gives scripts programmatic access to the user's text selection. Prior to Chrome 137, the that live inside shadow roots were invisible to getRangeAt(0) — you'd get an empty or truncated range.
The new getComposedRanges() method accepts a list of and returns a StaticRange that can cross those roots. The selection's direction property tells you whether the user dragged or backward, independent of the DOM order.
This unlocks real-world use cases: that highlight across component boundaries, accessible read-aloud tools that can speak selected text regardless of encapsulation, and document editors that need precise cursor placement inside custom elements.
// Real-time word counter using composed ranges
document.addEventListener('selectionchange', () => {
const sel = window.getSelection();
if (!sel || sel.isCollapsed) return;
const composed = sel.getComposedRanges({ shadowRoots: openShadowRoots });
const text = composed[0]?.toString() ?? '';
const words = text.trim().split(/\s+/).filter(Boolean).length;
const chars = text.length;
const dir = sel.direction; // "forward" | "backward" | "none"
// Compare with legacy API
const legacyText = sel.getRangeAt(0)?.toString() ?? '';
const crossShadow = text.length > legacyText.length;
// crossShadow === true means getRangeAt missed shadow content
});
see also
- Text Annotator — highlight across shadow boundaries
- Direction Detector — direction live readout
- Composed Ranges — basics