From 48af16367c92c0e5270a36aeba6408234b3779e5 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Tue, 14 Oct 2025 22:29:33 -0400 Subject: [PATCH 1/2] Measure text nodes --- .../src/backend/fiber/renderer.js | 18 ++++++++++++++++-- .../src/backend/views/Highlighter/Overlay.js | 7 +++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 6ff78a0ba685a..d023ba9286524 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -2251,7 +2251,10 @@ export function attach( if (typeof instance !== 'object' || instance === null) { return null; } - if (typeof instance.getClientRects === 'function') { + if ( + typeof instance.getClientRects === 'function' || + instance.nodeType === 3 + ) { // DOM const doc = instance.ownerDocument; if (instance === doc.documentElement) { @@ -2273,7 +2276,18 @@ export function attach( const win = doc && doc.defaultView; const scrollX = win ? win.scrollX : 0; const scrollY = win ? win.scrollY : 0; - const rects = instance.getClientRects(); + let rects; + if (instance.nodeType === 3) { + // Text nodes cannot be measured directly but we can measure a Range. + if (typeof doc.createRange !== 'function') { + return null; + } + const range = doc.createRange(); + range.selectNodeContents(instance); + rects = range.getClientRects(); + } else { + rects = instance.getClientRects(); + } for (let i = 0; i < rects.length; i++) { const rect = rects[i]; result.push({ diff --git a/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js b/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js index fdd059cc23486..e55bdb4298c47 100644 --- a/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js +++ b/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js @@ -187,10 +187,13 @@ export default class Overlay { } } - inspect(nodes: $ReadOnlyArray, name?: ?string) { + inspect(nodes: $ReadOnlyArray, name?: ?string) { // We can't get the size of text nodes or comment nodes. React as of v15 // heavily uses comment nodes to delimit text. - const elements = nodes.filter(node => node.nodeType === Node.ELEMENT_NODE); + // TODO: We actually can measure text nodes. We should. + const elements: $ReadOnlyArray = (nodes.filter( + node => node.nodeType === Node.ELEMENT_NODE, + ): any); while (this.rects.length > elements.length) { const rect = this.rects.pop(); From 789ed3415837d9728863cb075bf8acb758ba1b0c Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 15 Oct 2025 10:13:53 -0400 Subject: [PATCH 2/2] Feature test the range --- packages/react-devtools-shared/src/backend/fiber/renderer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index d023ba9286524..6e4426d3ee936 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -2283,6 +2283,9 @@ export function attach( return null; } const range = doc.createRange(); + if (typeof range.getClientRects !== 'function') { + return null; + } range.selectNodeContents(instance); rects = range.getClientRects(); } else {